From cfb9f874180c95f396ffc5a40fc508584c344802 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 5 Dec 2017 22:07:25 -0600 Subject: Fix Party element so that an identicon's height is that which was passed in --- packages/website/ts/components/ui/party.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx index 2927d9d3d..5bafa6071 100644 --- a/packages/website/ts/components/ui/party.tsx +++ b/packages/website/ts/components/ui/party.tsx @@ -73,7 +73,7 @@ export class Party extends React.Component { /> :
Date: Tue, 5 Dec 2017 22:15:23 -0600 Subject: Move declaration into proper conditional block --- packages/website/ts/blockchain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index c32984477..f11c014fb 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -511,7 +511,6 @@ export class Blockchain { const subscriptionId = this.zeroEx.exchange.subscribe( ExchangeEvents.LogFill, indexFilterValues, async (err: Error, decodedLogEvent: DecodedLogEvent) => { - const decodedLog = decodedLogEvent.log; if (err) { // Note: it's not entirely clear from the documentation which // errors will be thrown by `watch`. For now, let's log the error @@ -522,6 +521,7 @@ export class Blockchain { this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget return; } else { + const decodedLog = decodedLogEvent.log; if (!this.doesLogEventInvolveUser(decodedLog)) { return; // We aren't interested in the fill event } -- cgit From d8adc88c52efa5328f1bf61747201e3ddd06c451 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 5 Dec 2017 22:15:31 -0600 Subject: Add missing await --- packages/website/ts/components/ui/lifecycle_raised_button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/components/ui/lifecycle_raised_button.tsx b/packages/website/ts/components/ui/lifecycle_raised_button.tsx index 630f71545..338a3bf76 100644 --- a/packages/website/ts/components/ui/lifecycle_raised_button.tsx +++ b/packages/website/ts/components/ui/lifecycle_raised_button.tsx @@ -83,7 +83,7 @@ export class LifeCycleRaisedButton extends this.setState({ buttonState: ButtonState.LOADING, }); - const didSucceed = this.props.onClickAsyncFn(); + const didSucceed = await this.props.onClickAsyncFn(); if (this.didUnmount) { return; // noop since unmount called before async callback returned. } -- cgit From c452294bcc956d1af74a5ccd43152682cd436065 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 8 Dec 2017 13:06:24 -0600 Subject: Change dev domain since .dev is actually owned by Google and Chrome now enforces HSTS by default --- packages/website/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/README.md b/packages/website/README.md index 40df07cb7..128477372 100644 --- a/packages/website/README.md +++ b/packages/website/README.md @@ -20,7 +20,7 @@ Requires Node version 6.9.5 or higher. Add the following to your `/etc/hosts` file: ``` -127.0.0.1 0xproject.dev +127.0.0.1 0xproject.localhost ``` Clone the [0x contracts repo](https://github.com/0xProject/contracts) into the same parent directory as this project. @@ -45,7 +45,7 @@ Start dev server: yarn run dev ``` -Visit [0xproject.dev:3572](http://0xproject.dev:3572) in your browser. +Visit [0xproject.localhost:3572](http://0xproject.localhost:3572) in your browser. ##### Recommended Atom packages: -- cgit From ec3d8a034fe763d8255935985b1fb97aff6c177b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 8 Dec 2017 13:06:35 -0600 Subject: Update yarn.lock --- yarn.lock | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 245b67b33..3777c8ef2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1281,10 +1281,6 @@ bignumber.js@^4.0.2, bignumber.js@^4.1.0, bignumber.js@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1" -bignumber.js@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-5.0.0.tgz#fbce63f09776b3000a83185badcde525daf34833" - "bignumber.js@git+https://github.com/debris/bignumber.js#master": version "2.0.7" resolved "git+https://github.com/debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9" @@ -5629,7 +5625,7 @@ normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" -npm-run-all@^4.1.1, npm-run-all@^4.1.2: +npm-run-all@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.2.tgz#90d62d078792d20669139e718621186656cea056" dependencies: -- cgit From dc5694e54489ecf69d23b7ebbf085eae01491d66 Mon Sep 17 00:00:00 2001 From: Olaf Tomalka Date: Fri, 26 Jan 2018 18:22:32 +0100 Subject: Added CLI options for explicit specifying location of partials and main template --- packages/abi-gen/CHANGELOG.md | 1 + packages/abi-gen/src/index.ts | 63 +++++++++++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index fb2c6eb24..0f54c2480 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -2,6 +2,7 @@ ## v0.1.0 - _January 11, 2018_ +* Added CLI options for explicit specifying location of partials and main template * Fixed array typings with union types (#295) * Add event ABIs to context data passed to templates (#302) * Add constructor ABIs to context data passed to templates (#304) diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index 527af32b1..3628adff8 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -20,21 +20,53 @@ const ABI_TYPE_EVENT = 'event'; const MAIN_TEMPLATE_NAME = 'contract.mustache'; const args = yargs - .option('abiGlob', { + .option('abis', { + alias: ['abiGlob'], describe: 'Glob pattern to search for ABI JSON files', type: 'string', - demand: true, + demandOption: true, }) + .option('output', { + alias: ['o', 'out'], + describe: 'Folder where to put the output files', + type: 'string', + normalize: true, + demandOption: true, + }) + .option('partials', { + describe: 'Glob pattern for the partial template files', + type: 'string', + implies: 'template', + }) + .option('template', { + describe: 'Path for the main template file that will be used to generate each contract', + type: 'string', + normalize: true, + }) + .group(['templates'], 'Deprecated') .option('templates', { describe: 'Folder where to search for templates', type: 'string', - demand: true, }) - .option('output', { - describe: 'Folder where to put the output files', - type: 'string', - demand: true, - }).argv; + .conflicts('templates', ['partials', 'template-file']) + .check(argv => { + if (!argv.template && !argv.templates) { + throw new Error('You need specify the location of the template'); + } + return true; + }) + .example("$0 --abis 'src/artifacts/**/*.json' --out 'src/contracts/generated/' --partials 'src/templates/partials/**/*.handlebars' --template 'src/templates/contract.handlebars'", 'Full usage example') + .argv; + +function registerPartials(partialsGlob: string) { + const partialTemplateFileNames = globSync(partialsGlob); + utils.log(`Found ${chalk.green(`${partialTemplateFileNames.length}`)} ${chalk.bold('partial')} templates`); + for (const partialTemplateFileName of partialTemplateFileNames) { + const namedContent = utils.getNamedContent(partialTemplateFileName); + Handlebars.registerPartial(namedContent.name, namedContent.content); + } + return partialsGlob; +} function writeOutputFile(name: string, renderedTsCode: string): void { const fileName = toSnakeCase(name); @@ -45,15 +77,18 @@ function writeOutputFile(name: string, renderedTsCode: string): void { Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input)); Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output)); -const partialTemplateFileNames = globSync(`${args.templates}/partials/**/*.mustache`); -for (const partialTemplateFileName of partialTemplateFileNames) { - const namedContent = utils.getNamedContent(partialTemplateFileName); - Handlebars.registerPartial(namedContent.name, namedContent.content); + +if (args.partials) { + registerPartials(args.partials); +} +if (args.templates) { + registerPartials(`${args.templates}/partials/**/*.{mustache,handlebars}`); } -const mainTemplate = utils.getNamedContent(`${args.templates}/${MAIN_TEMPLATE_NAME}`); +const mainTemplate = utils.getNamedContent(args.template ? args.template : `${args.templates}/${MAIN_TEMPLATE_NAME}`); const template = Handlebars.compile(mainTemplate.content); -const abiFileNames = globSync(args.abiGlob); +const abiFileNames = globSync(args.abis); + if (_.isEmpty(abiFileNames)) { utils.log(`${chalk.red(`No ABI files found.`)}`); utils.log(`Please make sure you've passed the correct folder name and that the files have -- cgit From f37fcc147ccc45a234b062217321d26424c85a40 Mon Sep 17 00:00:00 2001 From: Olaf Tomalka Date: Fri, 26 Jan 2018 18:30:10 +0100 Subject: Added PR # to the changelog of abi-gen --- packages/abi-gen/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index 0f54c2480..9247b315a 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -1,8 +1,10 @@ # CHANGELOG +## v0.2.0 - _???_ +* Added CLI options for explicit specifying location of partials and main template (#346) + ## v0.1.0 - _January 11, 2018_ -* Added CLI options for explicit specifying location of partials and main template * Fixed array typings with union types (#295) * Add event ABIs to context data passed to templates (#302) * Add constructor ABIs to context data passed to templates (#304) -- cgit From b4faa4851a08fd526a6ffc546eed575b796a5b3d Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 28 Jan 2018 10:28:17 +0100 Subject: Properly detect user signing cancellation on Metamask, Parity signer and Ledger --- packages/website/ts/components/eth_weth_conversion_button.tsx | 2 +- packages/website/ts/components/fill_order.tsx | 4 ++-- packages/website/ts/components/inputs/allowance_toggle.tsx | 2 +- packages/website/ts/components/send_button.tsx | 2 +- packages/website/ts/components/token_balances.tsx | 2 +- packages/website/ts/utils/utils.ts | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index 300e71f1f..cc5e623ea 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -108,7 +108,7 @@ export class EthWethConversionButton extends React.Component< const errMsg = `${err}`; if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - } else if (!_.includes(errMsg, 'User denied transaction')) { + } else if (!utils.didUserDenyWeb3Request(errMsg)) { utils.consoleLog(`Unexpected error encountered: ${err}`); utils.consoleLog(err.stack); const errorMsg = diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 1a150e9ee..28488f399 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -573,7 +573,7 @@ export class FillOrder extends React.Component { isFilling: false, }); const errMsg = `${err}`; - if (_.includes(errMsg, 'User denied transaction signature')) { + if (utils.didUserDenyWeb3Request(errMsg)) { return; } globalErrMsg = 'Failed to fill order, please refresh and try again'; @@ -653,7 +653,7 @@ export class FillOrder extends React.Component { isCancelling: false, }); const errMsg = `${err}`; - if (_.includes(errMsg, 'User denied transaction signature')) { + if (utils.didUserDenyWeb3Request(errMsg)) { return; } globalErrMsg = 'Failed to cancel order, please refresh and try again'; diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx index da46db4f4..d8852c11f 100644 --- a/packages/website/ts/components/inputs/allowance_toggle.tsx +++ b/packages/website/ts/components/inputs/allowance_toggle.tsx @@ -78,7 +78,7 @@ export class AllowanceToggle extends React.Component Date: Sun, 28 Jan 2018 10:28:34 +0100 Subject: Add light blue as our accent color --- packages/website/ts/utils/mui_theme.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/website/ts/utils/mui_theme.ts b/packages/website/ts/utils/mui_theme.ts index d73e80606..32891baca 100644 --- a/packages/website/ts/utils/mui_theme.ts +++ b/packages/website/ts/utils/mui_theme.ts @@ -8,6 +8,7 @@ export const muiTheme = getMuiTheme({ textColor: colors.black, }, palette: { + accent1Color: colors.lightBlueA700, pickerHeaderColor: colors.lightBlue, primary1Color: colors.lightBlue, primary2Color: colors.lightBlue, -- cgit From 748d805a32ad7a1ee5f5a212d383b95460d3d828 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 28 Jan 2018 10:28:53 +0100 Subject: Remove unused Ledgerco import --- packages/website/package.json | 1 - packages/website/ts/globals.d.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/website/package.json b/packages/website/package.json index fcfa12301..e6d2c5f03 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -33,7 +33,6 @@ "find-versions": "^2.0.0", "is-mobile": "^0.2.2", "jsonschema": "^1.2.0", - "ledgerco": "0xProject/ledger-node-js-api", "less": "^2.7.2", "lodash": "^4.17.4", "material-ui": "^0.17.1", diff --git a/packages/website/ts/globals.d.ts b/packages/website/ts/globals.d.ts index 383e5cbe0..d7f887c6d 100644 --- a/packages/website/ts/globals.d.ts +++ b/packages/website/ts/globals.d.ts @@ -10,7 +10,6 @@ declare module 'thenby'; declare module 'react-highlight'; declare module 'react-recaptcha'; declare module 'react-document-title'; -declare module 'ledgerco'; declare module 'ethereumjs-tx'; declare module '*.json' { -- cgit From dd9f5adc2e771f3602461ae708d44536f146b902 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 28 Jan 2018 10:29:15 +0100 Subject: Initial Ledger support implementation --- packages/website/package.json | 9 +- packages/website/public/images/ledger_icon.png | Bin 0 -> 4885 bytes .../website/public/images/metamask_or_parity.png | Bin 0 -> 48180 bytes .../website/public/images/network_icons/kovan.png | Bin 0 -> 244 bytes .../public/images/network_icons/mainnet.png | Bin 0 -> 205 bytes .../website/public/images/network_icons/rinkby.png | Bin 0 -> 126 bytes .../public/images/network_icons/ropsten.png | Bin 0 -> 251 bytes packages/website/ts/blockchain.ts | 215 ++++++------ .../ts/components/dialogs/ledger_config_dialog.tsx | 46 ++- .../ts/components/dropdowns/network_drop_down.tsx | 40 +++ packages/website/ts/components/portal.tsx | 35 +- packages/website/ts/components/top_bar.tsx | 347 ------------------- .../ts/components/top_bar/provider_display.tsx | 149 ++++++++ .../ts/components/top_bar/provider_picker.tsx | 88 +++++ packages/website/ts/components/top_bar/top_bar.tsx | 377 +++++++++++++++++++++ .../ts/components/top_bar/top_bar_menu_item.tsx | 52 +++ .../website/ts/components/top_bar_menu_item.tsx | 52 --- packages/website/ts/components/ui/drop_down.tsx | 110 ++++++ .../ts/components/ui/drop_down_menu_item.tsx | 104 ------ packages/website/ts/containers/portal.tsx | 17 +- packages/website/ts/pages/about/about.tsx | 2 +- .../ts/pages/documentation/documentation.tsx | 2 +- packages/website/ts/pages/faq/faq.tsx | 2 +- packages/website/ts/pages/landing/landing.tsx | 2 +- packages/website/ts/pages/not_found.tsx | 2 +- packages/website/ts/pages/wiki/wiki.tsx | 2 +- packages/website/ts/types.ts | 5 + 27 files changed, 1027 insertions(+), 631 deletions(-) create mode 100644 packages/website/public/images/ledger_icon.png create mode 100644 packages/website/public/images/metamask_or_parity.png create mode 100644 packages/website/public/images/network_icons/kovan.png create mode 100644 packages/website/public/images/network_icons/mainnet.png create mode 100644 packages/website/public/images/network_icons/rinkby.png create mode 100644 packages/website/public/images/network_icons/ropsten.png create mode 100644 packages/website/ts/components/dropdowns/network_drop_down.tsx delete mode 100644 packages/website/ts/components/top_bar.tsx create mode 100644 packages/website/ts/components/top_bar/provider_display.tsx create mode 100644 packages/website/ts/components/top_bar/provider_picker.tsx create mode 100644 packages/website/ts/components/top_bar/top_bar.tsx create mode 100644 packages/website/ts/components/top_bar/top_bar_menu_item.tsx delete mode 100644 packages/website/ts/components/top_bar_menu_item.tsx create mode 100644 packages/website/ts/components/ui/drop_down.tsx delete mode 100644 packages/website/ts/components/ui/drop_down_menu_item.tsx diff --git a/packages/website/package.json b/packages/website/package.json index e6d2c5f03..38afa8ec1 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -8,9 +8,12 @@ "clean": "shx rm -f public/bundle*", "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", "dev": "webpack-dev-server --content-base public --https", - "update_contracts": "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;", - "deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", - "deploy_live": "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers" + "update_contracts": + "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;", + "deploy_staging": + "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", + "deploy_live": + "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers" }, "config": { "artifacts": "Mintable" diff --git a/packages/website/public/images/ledger_icon.png b/packages/website/public/images/ledger_icon.png new file mode 100644 index 000000000..29b8df08f Binary files /dev/null and b/packages/website/public/images/ledger_icon.png differ diff --git a/packages/website/public/images/metamask_or_parity.png b/packages/website/public/images/metamask_or_parity.png new file mode 100644 index 000000000..1085a0120 Binary files /dev/null and b/packages/website/public/images/metamask_or_parity.png differ diff --git a/packages/website/public/images/network_icons/kovan.png b/packages/website/public/images/network_icons/kovan.png new file mode 100644 index 000000000..f47a12e74 Binary files /dev/null and b/packages/website/public/images/network_icons/kovan.png differ diff --git a/packages/website/public/images/network_icons/mainnet.png b/packages/website/public/images/network_icons/mainnet.png new file mode 100644 index 000000000..6693635d6 Binary files /dev/null and b/packages/website/public/images/network_icons/mainnet.png differ diff --git a/packages/website/public/images/network_icons/rinkby.png b/packages/website/public/images/network_icons/rinkby.png new file mode 100644 index 000000000..f9ba18778 Binary files /dev/null and b/packages/website/public/images/network_icons/rinkby.png differ diff --git a/packages/website/public/images/network_icons/ropsten.png b/packages/website/public/images/network_icons/ropsten.png new file mode 100644 index 000000000..894910b34 Binary files /dev/null and b/packages/website/public/images/network_icons/ropsten.png differ diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 5530701c0..6fc56aecd 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -64,6 +64,7 @@ export class Blockchain { private _exchangeAddress: string; private _userAddress: string; private _cachedProvider: Web3.Provider; + private _cachedProviderNetworkId: number; private _ledgerSubprovider: LedgerWalletSubprovider; private _zrxPollIntervalId: NodeJS.Timer; private static async _onPageLoadAsync(): Promise { @@ -133,14 +134,14 @@ export class Blockchain { } else if (this.networkId !== newNetworkId) { this.networkId = newNetworkId; this._dispatcher.encounteredBlockchainError(BlockchainErrs.NoError); - await this._fetchTokenInformationAsync(); + await this.fetchTokenInformationAsync(); await this._rehydrateStoreWithContractEvents(); } } public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string) { if (this._userAddress !== newUserAddress) { this._userAddress = newUserAddress; - await this._fetchTokenInformationAsync(); + await this.fetchTokenInformationAsync(); await this._rehydrateStoreWithContractEvents(); } } @@ -180,63 +181,62 @@ export class Blockchain { } this._ledgerSubprovider.setPathIndex(pathIndex); } - public async providerTypeUpdatedFireAndForgetAsync(providerType: ProviderType) { + public async updateProviderToLedgerAsync(networkId: number) { utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - // Should actually be Web3.Provider|ProviderEngine union type but it causes issues - // later on in the logic. - let provider; - switch (providerType) { - case ProviderType.Ledger: { - const isU2FSupported = await utils.isU2FSupportedAsync(); - if (!isU2FSupported) { - throw new Error('Cannot update providerType to LEDGER without U2F support'); - } - // Cache injected provider so that we can switch the user back to it easily - this._cachedProvider = this._web3Wrapper.getProviderObj(); - - this._dispatcher.updateUserAddress(''); // Clear old userAddress - - provider = new ProviderEngine(); - const ledgerWalletConfigs = { - networkId: this.networkId, - ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, - }; - this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs); - provider.addProvider(this._ledgerSubprovider); - provider.addProvider(new FilterSubprovider()); - const networkId = configs.IS_MAINNET_ENABLED - ? constants.NETWORK_ID_MAINNET - : constants.NETWORK_ID_TESTNET; - provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); - provider.start(); - this._web3Wrapper.destroy(); - const shouldPollUserAddress = false; - this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); - this._zeroEx.setProvider(provider, networkId); - await this._postInstantiationOrUpdatingProviderZeroExAsync(); - break; - } + const isU2FSupported = await utils.isU2FSupportedAsync(); + if (!isU2FSupported) { + throw new Error('Cannot update providerType to LEDGER without U2F support'); + } - case ProviderType.Injected: { - if (_.isUndefined(this._cachedProvider)) { - return; // Going from injected to injected, so we noop - } - provider = this._cachedProvider; - const shouldPollUserAddress = true; - this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); - this._zeroEx.setProvider(provider, this.networkId); - await this._postInstantiationOrUpdatingProviderZeroExAsync(); - delete this._ledgerSubprovider; - delete this._cachedProvider; - break; - } + // Cache injected provider so that we can switch the user back to it easily + this._cachedProvider = this._web3Wrapper.getProviderObj(); + this._cachedProviderNetworkId = this.networkId; + + this._userAddress = ''; + this._dispatcher.updateUserAddress(''); // Clear old userAddress + + const provider = new ProviderEngine(); + const ledgerWalletConfigs = { + networkId, + ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, + }; + this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs); + provider.addProvider(this._ledgerSubprovider); + provider.addProvider(new FilterSubprovider()); + provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); + provider.start(); + this._web3Wrapper.destroy(); + this.networkId = networkId; + this._dispatcher.updateNetworkId(this.networkId); + const shouldPollUserAddress = false; + this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); + this._zeroEx.setProvider(provider, this.networkId); + await this._postInstantiationOrUpdatingProviderZeroExAsync(); + this._dispatcher.updateProviderType(ProviderType.Ledger); + } + public async updateProviderToInjectedAsync() { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - default: - throw utils.spawnSwitchErr('providerType', providerType); + if (_.isUndefined(this._cachedProvider)) { + return; // Going from injected to injected, so we noop } + const provider = this._cachedProvider; + this.networkId = this._cachedProviderNetworkId; + this._dispatcher.updateNetworkId(this.networkId); - await this._fetchTokenInformationAsync(); + this._web3Wrapper.destroy(); + const shouldPollUserAddress = true; + this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); + + this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); + this._dispatcher.updateUserAddress(this._userAddress); + this._zeroEx.setProvider(provider, this.networkId); + await this._postInstantiationOrUpdatingProviderZeroExAsync(); + await this.fetchTokenInformationAsync(); + this._dispatcher.updateProviderType(ProviderType.Injected); + delete this._ledgerSubprovider; + delete this._cachedProvider; } public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid); @@ -452,6 +452,7 @@ export class Blockchain { return [balance, allowance]; } public async updateTokenBalancesAndAllowancesAsync(tokens: Token[]) { + const err = new Error('show stopper'); const tokenStateByAddress: TokenStateByAddress = {}; for (const token of tokens) { let balance = new BigNumber(0); @@ -483,6 +484,60 @@ export class Blockchain { this._web3Wrapper.destroy(); this._stopWatchingExchangeLogFillEvents(); } + public async fetchTokenInformationAsync() { + utils.assert( + !_.isUndefined(this.networkId), + 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node', + ); + + this._dispatcher.updateBlockchainIsLoaded(false); + // HACK: Without this timeout, the second call to dispatcher somehow causes blockchainIsLoaded + // to flicker... Need to debug further :(((()))) + await new Promise(resolve => setTimeout(resolve, 100)); + this._dispatcher.clearTokenByAddress(); + + const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); + + // HACK: This is a hack so that the loading spinner doesn't show up twice... + // Once for loading the blockchain, another for loading the userAddress + this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); + if (!_.isEmpty(this._userAddress)) { + this._dispatcher.updateUserAddress(this._userAddress); + } + + let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId); + const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress); + if (_.isUndefined(trackedTokensIfExists)) { + trackedTokensIfExists = _.map(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => { + const token = _.find(tokenRegistryTokens, t => t.symbol === symbol); + token.isTracked = true; + return token; + }); + _.each(trackedTokensIfExists, token => { + trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token); + }); + } else { + // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array + _.each(trackedTokensIfExists, trackedToken => { + if (!_.isUndefined(tokenRegistryTokensByAddress[trackedToken.address])) { + tokenRegistryTokensByAddress[trackedToken.address].isTracked = true; + } + }); + } + const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]); + this._dispatcher.updateTokenByAddress(allTokens); + + // Get balance/allowance for tracked tokens + await this.updateTokenBalancesAndAllowancesAsync(trackedTokensIfExists); + + const mostPopularTradingPairTokens: Token[] = [ + _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }), + _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }), + ]; + this._dispatcher.updateChosenAssetTokenAddress(Side.Deposit, mostPopularTradingPairTokens[0].address); + this._dispatcher.updateChosenAssetTokenAddress(Side.Receive, mostPopularTradingPairTokens[1].address); + this._dispatcher.updateBlockchainIsLoaded(true); + } private async _showEtherScanLinkAndAwaitTransactionMinedAsync( txHash: string, ): Promise { @@ -690,60 +745,6 @@ export class Blockchain { : constants.PROVIDER_NAME_PUBLIC; this._dispatcher.updateInjectedProviderName(providerName); } - private async _fetchTokenInformationAsync() { - utils.assert( - !_.isUndefined(this.networkId), - 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node', - ); - - this._dispatcher.updateBlockchainIsLoaded(false); - this._dispatcher.clearTokenByAddress(); - - const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); - - // HACK: We need to fetch the userAddress here because otherwise we cannot save the - // tracked tokens in localStorage under the users address nor fetch the token - // balances and allowances and we need to do this in order not to trigger the blockchain - // loading dialog to show up twice. First to load the contracts, and second to load the - // balances and allowances. - this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); - if (!_.isEmpty(this._userAddress)) { - this._dispatcher.updateUserAddress(this._userAddress); - } - - let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId); - const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress); - if (_.isUndefined(trackedTokensIfExists)) { - trackedTokensIfExists = _.map(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => { - const token = _.find(tokenRegistryTokens, t => t.symbol === symbol); - token.isTracked = true; - return token; - }); - _.each(trackedTokensIfExists, token => { - trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token); - }); - } else { - // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array - _.each(trackedTokensIfExists, trackedToken => { - if (!_.isUndefined(tokenRegistryTokensByAddress[trackedToken.address])) { - tokenRegistryTokensByAddress[trackedToken.address].isTracked = true; - } - }); - } - const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]); - this._dispatcher.updateTokenByAddress(allTokens); - - // Get balance/allowance for tracked tokens - await this.updateTokenBalancesAndAllowancesAsync(trackedTokensIfExists); - - const mostPopularTradingPairTokens: Token[] = [ - _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }), - _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }), - ]; - this._dispatcher.updateChosenAssetTokenAddress(Side.Deposit, mostPopularTradingPairTokens[0].address); - this._dispatcher.updateChosenAssetTokenAddress(Side.Receive, mostPopularTradingPairTokens[1].address); - this._dispatcher.updateBlockchainIsLoaded(true); - } private async _instantiateContractIfExistsAsync(artifact: any, address?: string): Promise { const c = await contract(artifact); const providerObj = this._web3Wrapper.getProviderObj(); diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index 60db93c52..aff3f67b1 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -7,8 +7,10 @@ import TextField from 'material-ui/TextField'; import * as React from 'react'; import ReactTooltip = require('react-tooltip'); import { Blockchain } from 'ts/blockchain'; +import { NetworkDropDown } from 'ts/components/dropdowns/network_drop_down'; import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; import { Dispatcher } from 'ts/redux/dispatcher'; +import { ProviderType } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -27,27 +29,30 @@ interface LedgerConfigDialogProps { dispatcher: Dispatcher; blockchain: Blockchain; networkId: number; + providerType: ProviderType; } interface LedgerConfigDialogState { - didConnectFail: boolean; + connectionErrMsg: string; stepIndex: LedgerSteps; userAddresses: string[]; addressBalances: BigNumber[]; derivationPath: string; derivationErrMsg: string; + preferredNetworkId: number; } export class LedgerConfigDialog extends React.Component { constructor(props: LedgerConfigDialogProps) { super(props); this.state = { - didConnectFail: false, + connectionErrMsg: '', stepIndex: LedgerSteps.CONNECT, userAddresses: [], addressBalances: [], derivationPath: configs.DEFAULT_DERIVATION_PATH, derivationErrMsg: '', + preferredNetworkId: props.networkId, }; } public render() { @@ -77,7 +82,7 @@ export class LedgerConfigDialog extends React.Component
Follow these instructions before proceeding:
-
    +
    1. Connect your Ledger Nano S & Open the Ethereum application
    2. Verify that Browser Support is enabled in Settings
    3. @@ -86,7 +91,15 @@ export class LedgerConfigDialog extends React.Component1.2
    4. +
    5. Choose your desired network:
    +
    + +
    - {this.state.didConnectFail && ( + {!_.isEmpty(this.state.connectionErrMsg) && (
    - Failed to connect. Follow the instructions and try again. + {this.state.connectionErrMsg}
    )}
    @@ -172,7 +185,7 @@ export class LedgerConfigDialog extends React.Component void; + selectedNetworkId: number; + avialableNetworkIds: number[]; +} + +interface NetworkDropDownState {} + +export class NetworkDropDown extends React.Component { + public render() { + return ( +
    + + {this._renderDropDownItems()} + +
    + ); + } + private _renderDropDownItems() { + const items = _.map(this.props.avialableNetworkIds, networkId => { + const networkName = constants.NETWORK_NAME_BY_ID[networkId]; + const primaryText = ( +
    +
    + +
    +
    {networkName}
    +
    + ); + return ; + }); + return items; + } +} diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index e2e28e8b6..5975569c4 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -6,6 +6,7 @@ import * as DocumentTitle from 'react-document-title'; import { Route, Switch } from 'react-router-dom'; import { Blockchain } from 'ts/blockchain'; import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; +import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; import { WrappedEthSectionNoticeDialog } from 'ts/components/dialogs/wrapped_eth_section_notice_dialog'; import { EthWrappers } from 'ts/components/eth_wrappers'; @@ -13,19 +14,21 @@ import { FillOrder } from 'ts/components/fill_order'; import { Footer } from 'ts/components/footer'; import { PortalMenu } from 'ts/components/portal_menu'; import { TokenBalances } from 'ts/components/token_balances'; -import { TopBar } from 'ts/components/top_bar'; +import { TopBar } from 'ts/components/top_bar/top_bar'; import { TradeHistory } from 'ts/components/trade_history/trade_history'; import { FlashMessage } from 'ts/components/ui/flash_message'; import { Loading } from 'ts/components/ui/loading'; import { GenerateOrderForm } from 'ts/containers/generate_order_form'; import { localStorage } from 'ts/local_storage/local_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; import { orderSchema } from 'ts/schemas/order_schema'; import { SchemaValidator } from 'ts/schemas/validator'; import { BlockchainErrs, HashData, Order, + ProviderType, ScreenWidths, Token, TokenByAddress, @@ -46,9 +49,11 @@ export interface PortalAllProps { blockchainIsLoaded: boolean; dispatcher: Dispatcher; hashData: HashData; + injectedProviderName: string; networkId: number; nodeVersion: string; orderFillAmount: BigNumber; + providerType: ProviderType; screenWidth: ScreenWidths; tokenByAddress: TokenByAddress; tokenStateByAddress: TokenStateByAddress; @@ -67,6 +72,7 @@ interface PortalAllState { prevPathname: string; isDisclaimerDialogOpen: boolean; isWethNoticeDialogOpen: boolean; + isLedgerDialogOpen: boolean; } export class Portal extends React.Component { @@ -96,6 +102,7 @@ export class Portal extends React.Component { prevPathname: this.props.location.pathname, isDisclaimerDialogOpen: !hasAcceptedDisclaimer, isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, + isLedgerDialogOpen: false, }; } public componentDidMount() { @@ -127,8 +134,9 @@ export class Portal extends React.Component { this._blockchain.userAddressUpdatedFireAndForgetAsync(nextProps.userAddress); if (!_.isEmpty(nextProps.userAddress) && nextProps.blockchainIsLoaded) { const tokens = _.values(nextProps.tokenByAddress); + const trackedTokens = _.filter(tokens, t => t.isTracked); // tslint:disable-next-line:no-floating-promises - this._updateBalanceAndAllowanceWithLoadingScreenAsync(tokens); + this._updateBalanceAndAllowanceWithLoadingScreenAsync(trackedTokens); } this.setState({ prevUserAddress: nextProps.userAddress, @@ -167,8 +175,14 @@ export class Portal extends React.Component {
    @@ -239,11 +253,26 @@ export class Portal extends React.Component { onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)} /> + {this.props.blockchainIsLoaded && ( + + )}
    -
    +
    ;
); } + public onToggleLedgerDialog() { + this.setState({ + isLedgerDialogOpen: !this.state.isLedgerDialogOpen, + }); + } private _renderEthWrapper() { return ( { - public static defaultProps: Partial = { - shouldFullWidth: false, - style: {}, - isNightVersion: false, - }; - constructor(props: TopBarProps) { - super(props); - this.state = { - isDrawerOpen: false, - }; - } - public render() { - const isNightVersion = this.props.isNightVersion; - const isFullWidthPage = this.props.shouldFullWidth; - const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`; - const developerSectionMenuItems = [ - - - , - - - , - - - , - - - , - - - , - - - , - ]; - const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; - const fullWidthClasses = isFullWidthPage ? 'pr4' : ''; - const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png'; - const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; - const menuIconStyle = { - fontSize: 25, - color: isNightVersion ? 'white' : 'black', - cursor: 'pointer', - paddingTop: 16, - }; - return ( -
-
-
- - - -
-
-
- {!this._isViewingPortal() && ( -
-
- - - - -
-
- )} - {this.props.blockchainIsLoaded && - !_.isEmpty(this.props.userAddress) && ( -
{this._renderUser()}
- )} -
-
- -
-
-
- {this._renderDrawer()} -
- ); - } - private _renderDrawer() { - return ( - - {this._renderPortalMenu()} - {this._renderDocsMenu()} - {this._renderWiki()} -
- Website -
- - Home - - - Wiki - - {!this._isViewing0xjsDocs() && ( - - 0x.js Docs - - )} - {!this._isViewingConnectDocs() && ( - - 0x Connect Docs - - )} - {!this._isViewingSmartContractsDocs() && ( - - Smart Contract Docs - - )} - {!this._isViewingPortal() && ( - - Portal DApp - - )} - - Whitepaper - - - About - - - Blog - - - - FAQ - - -
- ); - } - private _renderDocsMenu(): React.ReactNode { - if ( - (!this._isViewing0xjsDocs() && !this._isViewingSmartContractsDocs() && !this._isViewingConnectDocs()) || - _.isUndefined(this.props.menu) - ) { - return undefined; - } - - const sectionTitle = `${this.props.docsInfo.displayName} Docs`; - return ( -
-
- {sectionTitle} -
- -
- ); - } - private _renderWiki(): React.ReactNode { - if (!this._isViewingWiki()) { - return undefined; - } - - return ( -
-
- 0x Protocol Wiki -
- -
- ); - } - private _renderPortalMenu(): React.ReactNode { - if (!this._isViewingPortal()) { - return undefined; - } - - return ( -
-
- Portal DApp -
- -
- ); - } - private _renderUser() { - const userAddress = this.props.userAddress; - const identiconDiameter = 26; - return ( -
-
- {!_.isEmpty(userAddress) ? userAddress : ''} -
- {userAddress} -
- -
-
- ); - } - private _onMenuButtonClick() { - this.setState({ - isDrawerOpen: !this.state.isDrawerOpen, - }); - } - private _isViewingPortal() { - return _.includes(this.props.location.pathname, WebsitePaths.Portal); - } - private _isViewingFAQ() { - return _.includes(this.props.location.pathname, WebsitePaths.FAQ); - } - private _isViewing0xjsDocs() { - return _.includes(this.props.location.pathname, WebsitePaths.ZeroExJs); - } - private _isViewingConnectDocs() { - return _.includes(this.props.location.pathname, WebsitePaths.Connect); - } - private _isViewingSmartContractsDocs() { - return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts); - } - private _isViewingWiki() { - return _.includes(this.props.location.pathname, WebsitePaths.Wiki); - } - private _shouldDisplayBottomBar() { - return ( - this._isViewingWiki() || - this._isViewing0xjsDocs() || - this._isViewingFAQ() || - this._isViewingSmartContractsDocs() || - this._isViewingConnectDocs() - ); - } -} diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx new file mode 100644 index 000000000..c7b6a4743 --- /dev/null +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -0,0 +1,149 @@ +import * as _ from 'lodash'; +import Menu from 'material-ui/Menu'; +import MenuItem from 'material-ui/MenuItem'; +import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import * as React from 'react'; +import { Link } from 'react-router-dom'; +import { Blockchain } from 'ts/blockchain'; +import { ProviderPicker } from 'ts/components/top_bar/provider_picker'; +import { DropDown } from 'ts/components/ui/drop_down'; +import { Identicon } from 'ts/components/ui/identicon'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { ProviderType } from 'ts/types'; +import { colors } from 'ts/utils/colors'; +import { constants } from 'ts/utils/constants'; +import { utils } from 'ts/utils/utils'; + +const IDENTICON_DIAMETER = 32; + +interface ProviderDisplayProps { + dispatcher: Dispatcher; + userAddress: string; + networkId: number; + injectedProviderName: string; + providerType: ProviderType; + onToggleLedgerDialog: () => void; + blockchain: Blockchain; +} + +interface ProviderDisplayState {} + +export class ProviderDisplay extends React.Component { + public render() { + const isAddressAvailable = !_.isEmpty(this.props.userAddress); + const isExternallyInjectedProvider = ProviderType.Injected && this.props.injectedProviderName !== '0x Public'; + const displayAddress = isAddressAvailable + ? utils.getAddressBeginAndEnd(this.props.userAddress) + : isExternallyInjectedProvider ? 'Account locked' : '0x0000...0000'; + // If the "injected" provider is our fallback public node, then we want to + // show the "connect a wallet" message instead of the providerName + const injectedProviderName = isExternallyInjectedProvider + ? this.props.injectedProviderName + : 'Connect a wallet'; + const providerTitle = + this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S'; + const hoverActiveNode = ( +
+
+ +
+
+
{providerTitle}
+
{displayAddress}
+
+
+ +
+
+ ); + const hasInjectedProvider = + this.props.injectedProviderName !== '0x Public' && this.props.providerType === ProviderType.Injected; + const hasLedgerProvider = this.props.providerType === ProviderType.Ledger; + const horizontalPosition = hasInjectedProvider || hasLedgerProvider ? 'left' : 'middle'; + return ( +
+ +
+ ); + } + public renderPopoverContent(hasInjectedProvider: boolean, hasLedgerProvider: boolean) { + if (hasInjectedProvider || hasLedgerProvider) { + return ( + + ); + } else { + // Nothing to connect to, show install/info popover + return ( +
+
+ Choose a wallet: +
+
+
+
Install a browser wallet
+
+ +
+
+ Use{' '} + + Metamask + {' '} + or{' '} + + Parity Signer + +
+
+
+
+
or
+
+
+
+
Connect to a ledger hardware wallet
+
+ +
+
+ +
+
+
+
+ ); + } + } +} diff --git a/packages/website/ts/components/top_bar/provider_picker.tsx b/packages/website/ts/components/top_bar/provider_picker.tsx new file mode 100644 index 000000000..ca98d8d05 --- /dev/null +++ b/packages/website/ts/components/top_bar/provider_picker.tsx @@ -0,0 +1,88 @@ +import * as _ from 'lodash'; +import Menu from 'material-ui/Menu'; +import MenuItem from 'material-ui/MenuItem'; +import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'; +import * as React from 'react'; +import { Link } from 'react-router-dom'; +import { Blockchain } from 'ts/blockchain'; +import { DropDown } from 'ts/components/ui/drop_down'; +import { Identicon } from 'ts/components/ui/identicon'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { ProviderType } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { utils } from 'ts/utils/utils'; + +const IDENTICON_DIAMETER = 32; +const SELECTED_BG_COLOR = '#F7F7F7'; + +interface ProviderPickerProps { + networkId: number; + injectedProviderName: string; + providerType: ProviderType; + onToggleLedgerDialog: () => void; + dispatcher: Dispatcher; + blockchain: Blockchain; +} + +interface ProviderPickerState {} + +export class ProviderPicker extends React.Component { + public render() { + const isLedgerSelected = this.props.providerType === ProviderType.Ledger; + const menuStyle = { + padding: 10, + paddingTop: 15, + paddingBottom: 15, + }; + const injectedLabel = ( +
+
{this.props.injectedProviderName}
+ {this._renderNetwork()} +
+ ); + // Show dropdown with two options + return ( +
+ + + + +
+ ); + } + private _renderNetwork() { + const networkName = constants.NETWORK_NAME_BY_ID[this.props.networkId]; + return ( +
+
+ +
+
{networkName}
+
+ ); + } + private _onProviderRadioChanged(e: any, value: string) { + if (value === ProviderType.Ledger) { + this.props.onToggleLedgerDialog(); + } else { + // Fire and forget + this.props.blockchain.updateProviderToInjectedAsync(); + } + } +} diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx new file mode 100644 index 000000000..652da5435 --- /dev/null +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -0,0 +1,377 @@ +import * as _ from 'lodash'; +import Drawer from 'material-ui/Drawer'; +import Menu from 'material-ui/Menu'; +import MenuItem from 'material-ui/MenuItem'; +import * as React from 'react'; +import { Link } from 'react-router-dom'; +import ReactTooltip = require('react-tooltip'); +import { Blockchain } from 'ts/blockchain'; +import { PortalMenu } from 'ts/components/portal_menu'; +import { ProviderDisplay } from 'ts/components/top_bar/provider_display'; +import { TopBarMenuItem } from 'ts/components/top_bar/top_bar_menu_item'; +import { DropDown } from 'ts/components/ui/drop_down'; +import { Identicon } from 'ts/components/ui/identicon'; +import { DocsInfo } from 'ts/pages/documentation/docs_info'; +import { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { DocsMenu, MenuSubsectionsBySection, ProviderType, Styles, TypeDocNode, WebsitePaths } from 'ts/types'; +import { colors } from 'ts/utils/colors'; +import { configs } from 'ts/utils/configs'; +import { constants } from 'ts/utils/constants'; + +interface TopBarProps { + userAddress?: string; + networkId?: number; + injectedProviderName?: string; + providerType?: ProviderType; + onToggleLedgerDialog?: () => void; + blockchain?: Blockchain; + dispatcher?: Dispatcher; + blockchainIsLoaded: boolean; + location: Location; + docsVersion?: string; + availableDocVersions?: string[]; + menu?: DocsMenu; + menuSubsectionsBySection?: MenuSubsectionsBySection; + shouldFullWidth?: boolean; + docsInfo?: DocsInfo; + style?: React.CSSProperties; + isNightVersion?: boolean; +} + +interface TopBarState { + isDrawerOpen: boolean; +} + +const styles: Styles = { + address: { + marginRight: 12, + overflow: 'hidden', + paddingTop: 4, + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + width: 70, + }, + topBar: { + backgroundcolor: colors.white, + height: 59, + width: '100%', + position: 'relative', + top: 0, + zIndex: 1100, + paddingBottom: 1, + }, + bottomBar: { + boxShadow: 'rgba(0, 0, 0, 0.187647) 0px 1px 3px', + }, + menuItem: { + fontSize: 14, + color: colors.darkestGrey, + paddingTop: 6, + paddingBottom: 6, + marginTop: 17, + cursor: 'pointer', + fontWeight: 400, + }, +}; + +export class TopBar extends React.Component { + public static defaultProps: Partial = { + shouldFullWidth: false, + style: {}, + isNightVersion: false, + }; + constructor(props: TopBarProps) { + super(props); + this.state = { + isDrawerOpen: false, + }; + } + public render() { + const isNightVersion = this.props.isNightVersion; + const isFullWidthPage = this.props.shouldFullWidth; + const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`; + const developerSectionMenuItems = [ + + + , + + + , + + + , + + + , + + + , + + + , + ]; + const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; + const fullWidthClasses = isFullWidthPage ? 'pr4' : ''; + const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png'; + const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; + const menuIconStyle = { + fontSize: 25, + color: isNightVersion ? 'white' : 'black', + cursor: 'pointer', + paddingTop: 16, + }; + const hoverActiveNode = ( +
+
Developers
+
+ +
+
+ ); + const popoverContent = {developerSectionMenuItems}; + return ( +
+
+
+ + + +
+
+
+ {!this._isViewingPortal() && ( +
+
+ + + + +
+
+ )} + {this.props.blockchainIsLoaded && ( +
+ +
+ )} +
+
+ +
+
+
+ {this._renderDrawer()} +
+ ); + } + private _renderDrawer() { + return ( + + {this._renderPortalMenu()} + {this._renderDocsMenu()} + {this._renderWiki()} +
+ Website +
+ + Home + + + Wiki + + {!this._isViewing0xjsDocs() && ( + + 0x.js Docs + + )} + {!this._isViewingConnectDocs() && ( + + 0x Connect Docs + + )} + {!this._isViewingSmartContractsDocs() && ( + + Smart Contract Docs + + )} + {!this._isViewingPortal() && ( + + Portal DApp + + )} + + Whitepaper + + + About + + + Blog + + + + FAQ + + +
+ ); + } + private _renderDocsMenu(): React.ReactNode { + if ( + (!this._isViewing0xjsDocs() && !this._isViewingSmartContractsDocs() && !this._isViewingConnectDocs()) || + _.isUndefined(this.props.menu) + ) { + return undefined; + } + + const sectionTitle = `${this.props.docsInfo.displayName} Docs`; + return ( +
+
+ {sectionTitle} +
+ +
+ ); + } + private _renderWiki(): React.ReactNode { + if (!this._isViewingWiki()) { + return undefined; + } + + return ( +
+
+ 0x Protocol Wiki +
+ +
+ ); + } + private _renderPortalMenu(): React.ReactNode { + if (!this._isViewingPortal()) { + return undefined; + } + + return ( +
+
+ Portal DApp +
+ +
+ ); + } + private _renderUser() { + const userAddress = this.props.userAddress; + const identiconDiameter = 26; + return ( +
+
+ {!_.isEmpty(userAddress) ? userAddress : ''} +
+ {userAddress} +
+ +
+
+ ); + } + private _onMenuButtonClick() { + this.setState({ + isDrawerOpen: !this.state.isDrawerOpen, + }); + } + private _isViewingPortal() { + return _.includes(this.props.location.pathname, WebsitePaths.Portal); + } + private _isViewingFAQ() { + return _.includes(this.props.location.pathname, WebsitePaths.FAQ); + } + private _isViewing0xjsDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.ZeroExJs); + } + private _isViewingConnectDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.Connect); + } + private _isViewingSmartContractsDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts); + } + private _isViewingWiki() { + return _.includes(this.props.location.pathname, WebsitePaths.Wiki); + } + private _shouldDisplayBottomBar() { + return ( + this._isViewingWiki() || + this._isViewing0xjsDocs() || + this._isViewingFAQ() || + this._isViewingSmartContractsDocs() || + this._isViewingConnectDocs() + ); + } +} diff --git a/packages/website/ts/components/top_bar/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx new file mode 100644 index 000000000..96ee86142 --- /dev/null +++ b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx @@ -0,0 +1,52 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import { Link } from 'react-router-dom'; +import { colors } from 'ts/utils/colors'; + +const DEFAULT_STYLE = { + color: colors.darkestGrey, +}; + +interface TopBarMenuItemProps { + title: string; + path?: string; + isPrimary?: boolean; + style?: React.CSSProperties; + className?: string; + isNightVersion?: boolean; +} + +interface TopBarMenuItemState {} + +export class TopBarMenuItem extends React.Component { + public static defaultProps: Partial = { + isPrimary: false, + style: DEFAULT_STYLE, + className: '', + isNightVersion: false, + }; + public render() { + const primaryStyles = this.props.isPrimary + ? { + borderRadius: 4, + border: `1px solid ${this.props.isNightVersion ? colors.grey : colors.greyishPink}`, + marginTop: 15, + paddingLeft: 9, + paddingRight: 9, + width: 77, + } + : {}; + const menuItemColor = this.props.isNightVersion ? 'white' : this.props.style.color; + const linkColor = _.isUndefined(menuItemColor) ? colors.darkestGrey : menuItemColor; + return ( +
+ + {this.props.title} + +
+ ); + } +} diff --git a/packages/website/ts/components/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar_menu_item.tsx deleted file mode 100644 index 96ee86142..000000000 --- a/packages/website/ts/components/top_bar_menu_item.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as _ from 'lodash'; -import * as React from 'react'; -import { Link } from 'react-router-dom'; -import { colors } from 'ts/utils/colors'; - -const DEFAULT_STYLE = { - color: colors.darkestGrey, -}; - -interface TopBarMenuItemProps { - title: string; - path?: string; - isPrimary?: boolean; - style?: React.CSSProperties; - className?: string; - isNightVersion?: boolean; -} - -interface TopBarMenuItemState {} - -export class TopBarMenuItem extends React.Component { - public static defaultProps: Partial = { - isPrimary: false, - style: DEFAULT_STYLE, - className: '', - isNightVersion: false, - }; - public render() { - const primaryStyles = this.props.isPrimary - ? { - borderRadius: 4, - border: `1px solid ${this.props.isNightVersion ? colors.grey : colors.greyishPink}`, - marginTop: 15, - paddingLeft: 9, - paddingRight: 9, - width: 77, - } - : {}; - const menuItemColor = this.props.isNightVersion ? 'white' : this.props.style.color; - const linkColor = _.isUndefined(menuItemColor) ? colors.darkestGrey : menuItemColor; - return ( -
- - {this.props.title} - -
- ); - } -} diff --git a/packages/website/ts/components/ui/drop_down.tsx b/packages/website/ts/components/ui/drop_down.tsx new file mode 100644 index 000000000..31a67f0d7 --- /dev/null +++ b/packages/website/ts/components/ui/drop_down.tsx @@ -0,0 +1,110 @@ +import * as _ from 'lodash'; +import Menu from 'material-ui/Menu'; +import Popover, { PopoverAnimationVertical } from 'material-ui/Popover'; +import * as React from 'react'; +import { MaterialUIPosition, Styles, WebsitePaths } from 'ts/types'; +import { colors } from 'ts/utils/colors'; + +const CHECK_CLOSE_POPOVER_INTERVAL_MS = 300; +const DEFAULT_STYLE = { + fontSize: 14, +}; + +interface DropDownProps { + hoverActiveNode: React.ReactNode; + popoverContent: React.ReactNode; + anchorOrigin: MaterialUIPosition; + targetOrigin: MaterialUIPosition; + style?: React.CSSProperties; + zDepth?: number; +} + +interface DropDownState { + isDropDownOpen: boolean; + anchorEl?: HTMLInputElement; +} + +export class DropDown extends React.Component { + public static defaultProps: Partial = { + style: DEFAULT_STYLE, + zDepth: 1, + }; + private _isHovering: boolean; + private _popoverCloseCheckIntervalId: number; + constructor(props: DropDownProps) { + super(props); + this.state = { + isDropDownOpen: false, + }; + } + public componentDidMount() { + this._popoverCloseCheckIntervalId = window.setInterval(() => { + this._checkIfShouldClosePopover(); + }, CHECK_CLOSE_POPOVER_INTERVAL_MS); + } + public componentWillUnmount() { + window.clearInterval(this._popoverCloseCheckIntervalId); + } + public componentWillReceiveProps(nextProps: DropDownProps) { + // HACK: If the popoverContent is updated to a different dimension and the users + // mouse is no longer above it, the dropdown can enter an inconsistent state where + // it believes the user is still hovering over it. In order to remedy this, we + // call hoverOff whenever the dropdown receives updated props. This is a hack + // because it will effectively close the dropdown on any prop update, barring + // dropdowns from having dynamic content. + this._onHoverOff(); + } + public render() { + return ( +
+ {this.props.hoverActiveNode} + +
+ {this.props.popoverContent} +
+
+
+ ); + } + private _onHover(event: React.FormEvent) { + this._isHovering = true; + this._checkIfShouldOpenPopover(event); + } + private _checkIfShouldOpenPopover(event: React.FormEvent) { + if (this.state.isDropDownOpen) { + return; // noop + } + + this.setState({ + isDropDownOpen: true, + anchorEl: event.currentTarget, + }); + } + private _onHoverOff() { + this._isHovering = false; + } + private _checkIfShouldClosePopover() { + if (!this.state.isDropDownOpen || this._isHovering) { + return; // noop + } + this._closePopover(); + } + private _closePopover() { + this.setState({ + isDropDownOpen: false, + }); + } +} diff --git a/packages/website/ts/components/ui/drop_down_menu_item.tsx b/packages/website/ts/components/ui/drop_down_menu_item.tsx deleted file mode 100644 index a578fb4f9..000000000 --- a/packages/website/ts/components/ui/drop_down_menu_item.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import * as _ from 'lodash'; -import Menu from 'material-ui/Menu'; -import Popover from 'material-ui/Popover'; -import * as React from 'react'; -import { colors } from 'ts/utils/colors'; - -const CHECK_CLOSE_POPOVER_INTERVAL_MS = 300; -const DEFAULT_STYLE = { - fontSize: 14, -}; - -interface DropDownMenuItemProps { - title: string; - subMenuItems: React.ReactNode[]; - style?: React.CSSProperties; - menuItemStyle?: React.CSSProperties; - isNightVersion?: boolean; -} - -interface DropDownMenuItemState { - isDropDownOpen: boolean; - anchorEl?: HTMLInputElement; -} - -export class DropDownMenuItem extends React.Component { - public static defaultProps: Partial = { - style: DEFAULT_STYLE, - menuItemStyle: DEFAULT_STYLE, - isNightVersion: false, - }; - private _isHovering: boolean; - private _popoverCloseCheckIntervalId: number; - constructor(props: DropDownMenuItemProps) { - super(props); - this.state = { - isDropDownOpen: false, - }; - } - public componentDidMount() { - this._popoverCloseCheckIntervalId = window.setInterval(() => { - this._checkIfShouldClosePopover(); - }, CHECK_CLOSE_POPOVER_INTERVAL_MS); - } - public componentWillUnmount() { - window.clearInterval(this._popoverCloseCheckIntervalId); - } - public render() { - const colorStyle = this.props.isNightVersion ? 'white' : this.props.style.color; - return ( -
-
-
{this.props.title}
-
- -
-
- -
- {this.props.subMenuItems} -
-
-
- ); - } - private _onHover(event: React.FormEvent) { - this._isHovering = true; - this._checkIfShouldOpenPopover(event); - } - private _checkIfShouldOpenPopover(event: React.FormEvent) { - if (this.state.isDropDownOpen) { - return; // noop - } - - this.setState({ - isDropDownOpen: true, - anchorEl: event.currentTarget, - }); - } - private _onHoverOff(event: React.FormEvent) { - this._isHovering = false; - } - private _checkIfShouldClosePopover() { - if (!this.state.isDropDownOpen || this._isHovering) { - return; // noop - } - this._closePopover(); - } - private _closePopover() { - this.setState({ - isDropDownOpen: false, - }); - } -} diff --git a/packages/website/ts/containers/portal.tsx b/packages/website/ts/containers/portal.tsx index f0247935b..2c515aac4 100644 --- a/packages/website/ts/containers/portal.tsx +++ b/packages/website/ts/containers/portal.tsx @@ -6,16 +6,27 @@ import { Dispatch } from 'redux'; import { Portal as PortalComponent, PortalAllProps as PortalComponentAllProps } from 'ts/components/portal'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { BlockchainErrs, HashData, Order, ScreenWidths, Side, TokenByAddress, TokenStateByAddress } from 'ts/types'; +import { + BlockchainErrs, + HashData, + Order, + ProviderType, + ScreenWidths, + Side, + TokenByAddress, + TokenStateByAddress, +} from 'ts/types'; import { constants } from 'ts/utils/constants'; interface ConnectedState { blockchainErr: BlockchainErrs; blockchainIsLoaded: boolean; hashData: HashData; + injectedProviderName: string; networkId: number; nodeVersion: string; orderFillAmount: BigNumber; + providerType: ProviderType; tokenByAddress: TokenByAddress; tokenStateByAddress: TokenStateByAddress; userEtherBalance: BigNumber; @@ -57,10 +68,12 @@ const mapStateToProps = (state: State, ownProps: PortalComponentAllProps): Conne return { blockchainErr: state.blockchainErr, blockchainIsLoaded: state.blockchainIsLoaded, + hashData, + injectedProviderName: state.injectedProviderName, networkId: state.networkId, nodeVersion: state.nodeVersion, orderFillAmount: state.orderFillAmount, - hashData, + providerType: state.providerType, screenWidth: state.screenWidth, shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, tokenByAddress: state.tokenByAddress, diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index c929673f5..0889e79f3 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; import { Footer } from 'ts/components/footer'; -import { TopBar } from 'ts/components/top_bar'; +import { TopBar } from 'ts/components/top_bar/top_bar'; import { Profile } from 'ts/pages/about/profile'; import { ProfileInfo, Styles } from 'ts/types'; import { colors } from 'ts/utils/colors'; diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx index 2315847ad..13a85c301 100644 --- a/packages/website/ts/pages/documentation/documentation.tsx +++ b/packages/website/ts/pages/documentation/documentation.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import DocumentTitle = require('react-document-title'); import { scroller } from 'react-scroll'; import semverSort = require('semver-sort'); -import { TopBar } from 'ts/components/top_bar'; +import { TopBar } from 'ts/components/top_bar/top_bar'; import { Badge } from 'ts/components/ui/badge'; import { Comment } from 'ts/pages/documentation/comment'; import { DocsInfo } from 'ts/pages/documentation/docs_info'; diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx index b4b5214a2..0a7eecc2d 100644 --- a/packages/website/ts/pages/faq/faq.tsx +++ b/packages/website/ts/pages/faq/faq.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; import { Footer } from 'ts/components/footer'; -import { TopBar } from 'ts/components/top_bar'; +import { TopBar } from 'ts/components/top_bar/top_bar'; import { Question } from 'ts/pages/faq/question'; import { FAQQuestion, FAQSection, Styles, WebsitePaths } from 'ts/types'; import { colors } from 'ts/utils/colors'; diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index ca76497df..b0c622fb4 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import DocumentTitle = require('react-document-title'); import { Link } from 'react-router-dom'; import { Footer } from 'ts/components/footer'; -import { TopBar } from 'ts/components/top_bar'; +import { TopBar } from 'ts/components/top_bar/top_bar'; import { ScreenWidths, WebsitePaths } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { constants } from 'ts/utils/constants'; diff --git a/packages/website/ts/pages/not_found.tsx b/packages/website/ts/pages/not_found.tsx index ff277c377..0a6ec071c 100644 --- a/packages/website/ts/pages/not_found.tsx +++ b/packages/website/ts/pages/not_found.tsx @@ -1,7 +1,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Footer } from 'ts/components/footer'; -import { TopBar } from 'ts/components/top_bar'; +import { TopBar } from 'ts/components/top_bar/top_bar'; import { Styles } from 'ts/types'; export interface NotFoundProps { diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index d065614ba..a3cf72450 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -3,7 +3,7 @@ import CircularProgress from 'material-ui/CircularProgress'; import * as React from 'react'; import DocumentTitle = require('react-document-title'); import { scroller } from 'react-scroll'; -import { TopBar } from 'ts/components/top_bar'; +import { TopBar } from 'ts/components/top_bar/top_bar'; import { MarkdownSection } from 'ts/pages/shared/markdown_section'; import { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu'; import { SectionHeader } from 'ts/pages/shared/section_header'; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index f873f95fa..aec8f7e1e 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -678,4 +678,9 @@ export enum SmartContractDocSections { ZRXToken = 'ZRXToken', } +export interface MaterialUIPosition { + vertical: 'bottom' | 'top' | 'center'; + horizontal: 'left' | 'middle' | 'right'; +} + // tslint:disable:max-file-line-count -- cgit From 6206ebc994a2cf76b90ac426218d6ed18b74a072 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 28 Jan 2018 16:19:55 +0100 Subject: Implement just-in-time loading of token balances & allowances --- packages/website/ts/blockchain.ts | 59 +++------ .../dialogs/eth_weth_conversion_dialog.tsx | 60 ++++++--- .../ts/components/dialogs/ledger_config_dialog.tsx | 3 +- .../website/ts/components/dialogs/send_dialog.tsx | 13 +- .../dialogs/track_token_confirmation_dialog.tsx | 10 -- .../ts/components/eth_weth_conversion_button.tsx | 14 +- packages/website/ts/components/eth_wrappers.tsx | 86 ++++++++++-- packages/website/ts/components/fill_order.tsx | 17 ++- .../ts/components/generate_order/asset_picker.tsx | 14 +- .../generate_order/generate_order_form.tsx | 20 ++- .../components/generate_order/new_token_form.tsx | 14 +- .../ts/components/inputs/allowance_toggle.tsx | 5 +- .../ts/components/inputs/balance_bounded_input.tsx | 3 + .../ts/components/inputs/token_amount_input.tsx | 54 +++++++- packages/website/ts/components/portal.tsx | 22 +--- packages/website/ts/components/send_button.tsx | 13 +- packages/website/ts/components/token_balances.tsx | 146 ++++++++++++++++----- .../ts/components/top_bar/provider_picker.tsx | 2 +- .../website/ts/containers/generate_order_form.tsx | 4 +- packages/website/ts/containers/portal.tsx | 4 +- packages/website/ts/redux/dispatcher.ts | 38 +----- packages/website/ts/redux/reducer.ts | 72 +--------- packages/website/ts/types.ts | 6 +- 23 files changed, 384 insertions(+), 295 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 6fc56aecd..898cb2d01 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -66,7 +66,6 @@ export class Blockchain { private _cachedProvider: Web3.Provider; private _cachedProviderNetworkId: number; private _ledgerSubprovider: LedgerWalletSubprovider; - private _zrxPollIntervalId: NodeJS.Timer; private static async _onPageLoadAsync(): Promise { if (document.readyState === 'complete') { return; // Already loaded @@ -250,7 +249,6 @@ export class Blockchain { ); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); const allowance = amountInBaseUnits; - this._dispatcher.replaceTokenAllowanceByAddress(token.address, allowance); } public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise { const txHash = await this._zeroEx.token.transferAsync( @@ -368,22 +366,25 @@ 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); - intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); - delete this._zrxPollIntervalId; - } - }, - 5000, - (err: Error) => { - utils.consoleLog(`Polling tokenBalance failed: ${err}`); - intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); - delete this._zrxPollIntervalId; - }, - ); + const newTokenBalancePromise = new Promise((resolve: (balance: BigNumber) => void, reject) => { + const tokenPollInterval = intervalUtils.setAsyncExcludingInterval( + async () => { + const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); + if (!balance.eq(currBalance)) { + intervalUtils.clearAsyncExcludingInterval(tokenPollInterval); + resolve(balance); + } + }, + 5000, + (err: Error) => { + utils.consoleLog(`Polling tokenBalance failed: ${err}`); + intervalUtils.clearAsyncExcludingInterval(tokenPollInterval); + reject(err); + }, + ); + }); + + return newTokenBalancePromise; } public async signOrderHashAsync(orderHash: string): Promise { utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); @@ -408,7 +409,6 @@ export class Blockchain { from: this._userAddress, }); const balanceDelta = constants.MINT_AMOUNT; - this._dispatcher.updateTokenBalanceByAddress(token.address, balanceDelta); } public async getBalanceInEthAsync(owner: string): Promise { const balance = await this._web3Wrapper.getBalanceInEthAsync(owner); @@ -451,23 +451,6 @@ export class Blockchain { } return [balance, allowance]; } - public async updateTokenBalancesAndAllowancesAsync(tokens: Token[]) { - const err = new Error('show stopper'); - const tokenStateByAddress: TokenStateByAddress = {}; - for (const token of tokens) { - let balance = new BigNumber(0); - let allowance = new BigNumber(0); - if (this._doesUserAddressExist()) { - [balance, allowance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); - } - const tokenState = { - balance, - allowance, - }; - tokenStateByAddress[token.address] = tokenState; - } - this._dispatcher.updateTokenStateByAddress(tokenStateByAddress); - } public async getUserAccountsAsync() { utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); const userAccountsIfExists = await this._zeroEx.getAvailableAddressesAsync(); @@ -480,7 +463,6 @@ export class Blockchain { this._web3Wrapper.updatePrevUserAddress(newUserAddress); } public destroy() { - intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); this._web3Wrapper.destroy(); this._stopWatchingExchangeLogFillEvents(); } @@ -527,9 +509,6 @@ export class Blockchain { const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]); this._dispatcher.updateTokenByAddress(allTokens); - // Get balance/allowance for tracked tokens - await this.updateTokenBalancesAndAllowancesAsync(trackedTokensIfExists); - const mostPopularTradingPairTokens: Token[] = [ _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }), _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }), diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx index 661cc1d8c..a3a39a1b9 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -2,25 +2,31 @@ import { BigNumber } from '@0xproject/utils'; import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; import * as React from 'react'; +import { Blockchain } from 'ts/blockchain'; import { EthAmountInput } from 'ts/components/inputs/eth_amount_input'; import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; -import { Side, Token, TokenState } from 'ts/types'; +import { Side, Token } from 'ts/types'; import { colors } from 'ts/utils/colors'; interface EthWethConversionDialogProps { + blockchain: Blockchain; + userAddress: string; + networkId: number; direction: Side; onComplete: (direction: Side, value: BigNumber) => void; onCancelled: () => void; isOpen: boolean; token: Token; - tokenState: TokenState; etherBalance: BigNumber; + lastForceTokenStateRefetch: number; } interface EthWethConversionDialogState { value?: BigNumber; shouldShowIncompleteErrs: boolean; hasErrors: boolean; + isEthTokenBalanceLoaded: boolean; + ethTokenBalance: BigNumber; } export class EthWethConversionDialog extends React.Component< @@ -32,8 +38,14 @@ export class EthWethConversionDialog extends React.Component< this.state = { shouldShowIncompleteErrs: false, hasErrors: false, + isEthTokenBalanceLoaded: false, + ethTokenBalance: new BigNumber(0), }; } + public componentWillMount() { + // tslint:disable-next-line:no-floating-promises + this._fetchEthTokenBalanceAsync(); + } public render() { const convertDialogActions = [ , @@ -72,8 +84,11 @@ export class EthWethConversionDialog extends React.Component<
{this.props.direction === Side.Receive ? (
1 ETH = 1 WETH
- {this.props.direction === Side.Receive && ( -
- Max -
- )} + {this.props.direction === Side.Receive && + this.state.isEthTokenBalanceLoaded && ( +
+ Max +
+ )}
@@ -132,7 +148,7 @@ export class EthWethConversionDialog extends React.Component< } private _onMaxClick() { this.setState({ - value: this.props.tokenState.balance, + value: this.state.ethTokenBalance, }); } private _onValueChange(isValid: boolean, amount?: BigNumber) { @@ -160,4 +176,14 @@ export class EthWethConversionDialog extends React.Component< }); this.props.onCancelled(); } + private async _fetchEthTokenBalanceAsync() { + const [balance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + this.props.userAddress, + this.props.token.address, + ); + this.setState({ + isEthTokenBalanceLoaded: true, + ethTokenBalance: balance, + }); + } } diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index aff3f67b1..a17a51622 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -197,7 +197,8 @@ export class LedgerConfigDialog extends React.Component void; onCancelled: () => void; isOpen: boolean; token: Token; - tokenState: TokenState; + lastForceTokenStateRefetch: number; } interface SendDialogState { @@ -66,15 +70,18 @@ export class SendDialog extends React.Component
); diff --git a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx index 3f29d46f8..bb7e3ed1a 100644 --- a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx +++ b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx @@ -82,16 +82,6 @@ export class TrackTokenConfirmationDialog extends React.Component< newTokenEntry.isTracked = true; trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry); this.props.dispatcher.updateTokenByAddress([newTokenEntry]); - - const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync( - token.address, - ); - this.props.dispatcher.updateTokenStateByAddress({ - [token.address]: { - balance, - allowance, - }, - }); } this.setState({ diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index cc5e623ea..52240fd0f 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -12,6 +12,8 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; interface EthWethConversionButtonProps { + userAddress: string; + networkId: number; direction: Side; ethToken: Token; ethTokenState: TokenState; @@ -21,6 +23,8 @@ interface EthWethConversionButtonProps { isOutdatedWrappedEther: boolean; onConversionSuccessful?: () => void; isDisabled?: boolean; + lastForceTokenStateRefetch: number; + refetchEthTokenStateAsync: () => Promise; } interface EthWethConversionButtonState { @@ -64,13 +68,16 @@ export class EthWethConversionButton extends React.Component< onClick={this._toggleConversionDialog.bind(this)} />
); @@ -87,21 +94,18 @@ export class EthWethConversionButton extends React.Component< this._toggleConversionDialog(); const token = this.props.ethToken; const tokenState = this.props.ethTokenState; - let balance = tokenState.balance; try { if (direction === Side.Deposit) { await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value); const ethAmount = ZeroEx.toUnitAmount(value, constants.DECIMAL_PLACES_ETH); this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`); - balance = balance.plus(value); } else { await this.props.blockchain.convertWrappedEthTokensToEthAsync(token.address, value); const tokenAmount = ZeroEx.toUnitAmount(value, token.decimals); this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`); - balance = balance.minus(value); } if (!this.props.isOutdatedWrappedEther) { - this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance); + this.props.refetchEthTokenStateAsync(); } this.props.onConversionSuccessful(); } catch (err) { diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index d074ec787..460a6cae3 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -41,12 +41,14 @@ interface EthWrappersProps { blockchain: Blockchain; dispatcher: Dispatcher; tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; userAddress: string; userEtherBalance: BigNumber; + lastForceTokenStateRefetch: number; } interface EthWrappersState { + ethTokenState: TokenState; + isWethStateLoaded: boolean; outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded; outdatedWETHStateByAddress: OutdatedWETHStateByAddress; } @@ -67,18 +69,31 @@ export class EthWrappers extends React.Component {this._renderTokenLink(tokenLabel, etherscanUrl)} - {wethBalance.toFixed(PRECISION)} WETH + + {this.state.isWethStateLoaded ? ( + `${wethBalance.toFixed(PRECISION)} WETH` + ) : ( + + )} + - {this._renderOutdatedWeths(etherToken, etherTokenState)} + {this._renderOutdatedWeths(etherToken, this.state.ethTokenState)}
@@ -269,6 +299,10 @@ export class EthWrappers extends React.Component token.symbol === 'WETH'); + const [wethBalance, wethAllowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + this.props.userAddress, + wethToken.address, + ); + const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; @@ -356,6 +397,11 @@ export class EthWrappers extends React.Component { symbol: takerToken.symbol, }; const fillToken = this.props.tokenByAddress[takerToken.address]; - const fillTokenState = this.props.tokenStateByAddress[takerToken.address]; const makerTokenAddress = this.state.parsedOrder.maker.token.address; const makerToken = this.props.tokenByAddress[makerTokenAddress]; const makerAssetToken = { @@ -249,14 +248,17 @@ export class FillOrder extends React.Component { {!isUserMaker && (
{ signedOrder, this.props.orderFillAmount, ); - // After fill completes, let's update the token balances - const makerToken = this.props.tokenByAddress[parsedOrder.maker.token.address]; - const takerToken = this.props.tokenByAddress[parsedOrder.taker.token.address]; - const tokens = [makerToken, takerToken]; - await this.props.blockchain.updateTokenBalancesAndAllowancesAsync(tokens); + // After fill completes, let's force fetch the token balances + this.props.dispatcher.forceTokenStateRefetch(); this.setState({ isFilling: false, didFillOrderSucceed: true, diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx index df7d87cfd..8d4eaab40 100644 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ b/packages/website/ts/components/generate_order/asset_picker.tsx @@ -223,10 +223,7 @@ export class AssetPicker extends React.Component void; + onNewTokenSubmitted: (token: Token) => void; } interface NewTokenFormState { @@ -110,13 +110,9 @@ export class NewTokenForm extends React.Component Promise; } interface AllowanceToggleState { @@ -45,7 +47,7 @@ export class AllowanceToggle extends React.Component
@@ -73,6 +75,7 @@ export class AllowanceToggle extends React.Component InputErrMsg; onVisitBalancesPageClick?: () => void; shouldHideVisitBalancesLink?: boolean; + isDisabled?: boolean; } interface BalanceBoundedInputState { @@ -29,6 +30,7 @@ export class BalanceBoundedInput extends React.Component = { shouldShowIncompleteErrs: false, shouldHideVisitBalancesLink: false, + isDisabled: false, }; constructor(props: BalanceBoundedInputProps) { super(props); @@ -88,6 +90,7 @@ export class BalanceBoundedInput extends React.Componentamount} onChange={this._onValueChange.bind(this)} underlineStyle={{ width: 'calc(100% + 50px)' }} + disabled={this.props.isDisabled} /> ); } diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index 63966d759..f41d42d02 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -3,13 +3,16 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as React from 'react'; import { Link } from 'react-router-dom'; +import { Blockchain } from 'ts/blockchain'; import { BalanceBoundedInput } from 'ts/components/inputs/balance_bounded_input'; import { InputErrMsg, Token, TokenState, ValidatedBigNumberCallback, WebsitePaths } from 'ts/types'; import { colors } from 'ts/utils/colors'; interface TokenAmountInputProps { + userAddress: string; + networkId: number; + blockchain: Blockchain; token: Token; - tokenState: TokenState; label?: string; amount?: BigNumber; shouldShowIncompleteErrs: boolean; @@ -17,11 +20,39 @@ interface TokenAmountInputProps { shouldCheckAllowance: boolean; onChange: ValidatedBigNumberCallback; onVisitBalancesPageClick?: () => void; + lastForceTokenStateRefetch: number; } -interface TokenAmountInputState {} +interface TokenAmountInputState { + balance: BigNumber; + allowance: BigNumber; + isBalanceAndAllowanceLoaded: boolean; +} export class TokenAmountInput extends React.Component { + constructor(props: TokenAmountInputProps) { + super(props); + const defaultAmount = new BigNumber(0); + this.state = { + balance: defaultAmount, + allowance: defaultAmount, + isBalanceAndAllowanceLoaded: false, + }; + } + public componentWillMount() { + // tslint:disable-next-line:no-floating-promises + this._fetchBalanceAndAllowanceAsync(this.props.token.address); + } + public componentWillReceiveProps(nextProps: TokenAmountInputProps) { + if ( + nextProps.userAddress !== this.props.userAddress || + nextProps.networkId !== this.props.networkId || + nextProps.token.address !== this.props.token.address || + nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch + ) { + this._fetchBalanceAndAllowanceAsync(nextProps.token.address); + } + } public render() { const amount = this.props.amount ? ZeroEx.toUnitAmount(this.props.amount, this.props.token.decimals) @@ -32,12 +63,13 @@ export class TokenAmountInput extends React.Component
{this.props.token.symbol}
@@ -51,7 +83,7 @@ export class TokenAmountInput extends React.Component Insufficient allowance.{' '} @@ -67,4 +99,18 @@ export class TokenAmountInput extends React.Component { if (nextProps.userAddress !== this.state.prevUserAddress) { // tslint:disable-next-line:no-floating-promises this._blockchain.userAddressUpdatedFireAndForgetAsync(nextProps.userAddress); - if (!_.isEmpty(nextProps.userAddress) && nextProps.blockchainIsLoaded) { - const tokens = _.values(nextProps.tokenByAddress); - const trackedTokens = _.filter(tokens, t => t.isTracked); - // tslint:disable-next-line:no-floating-promises - this._updateBalanceAndAllowanceWithLoadingScreenAsync(trackedTokens); - } this.setState({ prevUserAddress: nextProps.userAddress, }); @@ -280,9 +274,9 @@ export class Portal extends React.Component { blockchain={this._blockchain} dispatcher={this.props.dispatcher} tokenByAddress={this.props.tokenByAddress} - tokenStateByAddress={this.props.tokenStateByAddress} userAddress={this.props.userAddress} userEtherBalance={this.props.userEtherBalance} + lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} /> ); } @@ -296,6 +290,8 @@ export class Portal extends React.Component { ); } private _renderTokenBalances() { + const allTokens = _.values(this.props.tokenByAddress); + const trackedTokens = _.filter(allTokens, t => t.isTracked); return ( { dispatcher={this.props.dispatcher} screenWidth={this.props.screenWidth} tokenByAddress={this.props.tokenByAddress} - tokenStateByAddress={this.props.tokenStateByAddress} + trackedTokens={trackedTokens} userAddress={this.props.userAddress} userEtherBalance={this.props.userEtherBalance} networkId={this.props.networkId} + lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} /> ); } @@ -325,8 +322,8 @@ export class Portal extends React.Component { networkId={this.props.networkId} userAddress={this.props.userAddress} tokenByAddress={this.props.tokenByAddress} - tokenStateByAddress={this.props.tokenStateByAddress} dispatcher={this.props.dispatcher} + lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} /> ); } @@ -382,9 +379,4 @@ export class Portal extends React.Component { const newScreenWidth = utils.getScreenWidth(); this.props.dispatcher.updateScreenWidth(newScreenWidth); } - private async _updateBalanceAndAllowanceWithLoadingScreenAsync(tokens: Token[]) { - this.props.dispatcher.updateBlockchainIsLoaded(false); - await this._blockchain.updateTokenBalancesAndAllowancesAsync(tokens); - this.props.dispatcher.updateBlockchainIsLoaded(true); - } } diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx index 7c0c7c083..f13e8ecce 100644 --- a/packages/website/ts/components/send_button.tsx +++ b/packages/website/ts/components/send_button.tsx @@ -10,11 +10,15 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; interface SendButtonProps { + userAddress: string; + networkId: number; token: Token; tokenState: TokenState; dispatcher: Dispatcher; blockchain: Blockchain; onError: () => void; + lastForceTokenStateRefetch: number; + refetchTokenStateAsync: (tokenAddress: string) => Promise; } interface SendButtonState { @@ -42,11 +46,14 @@ export class SendButton extends React.Component
); @@ -63,11 +70,9 @@ export class SendButton extends React.Component { public constructor(props: TokenBalancesProps) { super(props); + const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens); this.state = { errorType: undefined, isBalanceSpinnerVisible: false, @@ -91,8 +100,14 @@ export class TokenBalances extends React.Component t.symbol === ZRX_TOKEN_SYMBOL); - const nextZrxTokenBalance = nextProps.tokenStateByAddress[nextZrxToken.address].balance; - if (!_.isUndefined(this.state.currentZrxBalance) && !nextZrxTokenBalance.eq(this.state.currentZrxBalance)) { - if (this.state.isZRXSpinnerVisible) { - const receivedAmount = nextZrxTokenBalance.minus(this.state.currentZrxBalance); - const receiveAmountInUnits = ZeroEx.toUnitAmount(receivedAmount, constants.DECIMAL_PLACES_ZRX); - this.props.dispatcher.showFlashMessage(`Received ${receiveAmountInUnits.toString(10)} Kovan ZRX`); - } - this.setState({ - isZRXSpinnerVisible: false, - currentZrxBalance: undefined, - }); + + if ( + nextProps.userAddress !== this.props.userAddress || + nextProps.networkId !== this.props.networkId || + nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch + ) { + const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); + // tslint:disable-next-line:no-floating-promises + this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); + } + + if (!_.isEqual(nextProps.trackedTokens, this.props.trackedTokens)) { + const newTokens = _.difference(nextProps.trackedTokens, this.props.trackedTokens); + const newTokenAddresses = _.map(newTokens, token => token.address); + this._fetchBalancesAndAllowancesAsync(newTokenAddresses); } } public componentDidMount() { @@ -303,8 +321,7 @@ export class TokenBalances extends React.Component t.isTracked); + const trackedTokens = this.props.trackedTokens; const trackedTokensStartingWithEtherToken = trackedTokens.sort( firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) @@ -317,7 +334,7 @@ export class TokenBalances extends React.Component - {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol} - {this.state.isZRXSpinnerVisible && - token.symbol === ZRX_TOKEN_SYMBOL && ( - - - - )} + {tokenState.isLoaded ? ( + + {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol} + {this.state.isZRXSpinnerVisible && + token.symbol === ZRX_TOKEN_SYMBOL && ( + + + + )} + + ) : ( + + )} @@ -383,11 +408,15 @@ export class TokenBalances extends React.Component )} @@ -414,7 +443,6 @@ export class TokenBalances extends React.Component { try { await this.props.blockchain.mintTestTokensAsync(token); + await this._refetchTokenStateAsync(token.address); const amount = ZeroEx.toUnitAmount(constants.MINT_AMOUNT, token.decimals); this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`); return true; @@ -569,15 +598,11 @@ export class TokenBalances extends React.Component t.symbol === ZRX_TOKEN_SYMBOL); - const zrxTokenState = this.props.tokenStateByAddress[zrxToken.address]; this.setState({ isZRXSpinnerVisible: true, - currentZrxBalance: zrxTokenState.balance, }); // tslint:disable-next-line:no-floating-promises - this.props.blockchain.pollTokenBalanceAsync(zrxToken); + this._startPollingZrxBalanceAsync(); } return true; } @@ -603,4 +628,63 @@ export class TokenBalances extends React.Component t.symbol === ZRX_TOKEN_SYMBOL); + + // tslint:disable-next-line:no-floating-promises + const balance = await this.props.blockchain.pollTokenBalanceAsync(zrxToken); + const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + trackedTokenStateByAddress[zrxToken.address] = { + ...trackedTokenStateByAddress[zrxToken.address], + balance, + }; + this.setState({ + isZRXSpinnerVisible: false, + }); + } + private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]) { + const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + for (const tokenAddress of tokenAddresses) { + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + this.props.userAddress, + tokenAddress, + ); + trackedTokenStateByAddress[tokenAddress] = { + balance, + allowance, + isLoaded: true, + }; + } + this.setState({ + trackedTokenStateByAddress, + }); + } + private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]) { + const trackedTokenStateByAddress: TokenStateByAddress = {}; + _.each(trackedTokens, token => { + trackedTokenStateByAddress[token.address] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + isLoaded: false, + }; + }); + return trackedTokenStateByAddress; + } + private async _refetchTokenStateAsync(tokenAddress: string) { + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + this.props.userAddress, + tokenAddress, + ); + this.setState({ + trackedTokenStateByAddress: { + ...this.state.trackedTokenStateByAddress, + [tokenAddress]: { + balance, + allowance, + isLoaded: true, + }, + }, + }); + } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/top_bar/provider_picker.tsx b/packages/website/ts/components/top_bar/provider_picker.tsx index ca98d8d05..418f8696b 100644 --- a/packages/website/ts/components/top_bar/provider_picker.tsx +++ b/packages/website/ts/components/top_bar/provider_picker.tsx @@ -81,7 +81,7 @@ export class ProviderPicker extends React.Component ({ @@ -45,8 +45,8 @@ const mapStateToProps = (state: State, ownProps: GenerateOrderFormProps): Connec networkId: state.networkId, sideToAssetToken: state.sideToAssetToken, tokenByAddress: state.tokenByAddress, - tokenStateByAddress: state.tokenStateByAddress, userAddress: state.userAddress, + lastForceTokenStateRefetch: state.lastForceTokenStateRefetch, }); export const GenerateOrderForm: React.ComponentClass = connect(mapStateToProps)( diff --git a/packages/website/ts/containers/portal.tsx b/packages/website/ts/containers/portal.tsx index 2c515aac4..9afe7fca3 100644 --- a/packages/website/ts/containers/portal.tsx +++ b/packages/website/ts/containers/portal.tsx @@ -28,7 +28,7 @@ interface ConnectedState { orderFillAmount: BigNumber; providerType: ProviderType; tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; + lastForceTokenStateRefetch: number; userEtherBalance: BigNumber; screenWidth: ScreenWidths; shouldBlockchainErrDialogBeOpen: boolean; @@ -77,7 +77,7 @@ const mapStateToProps = (state: State, ownProps: PortalComponentAllProps): Conne screenWidth: state.screenWidth, shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, tokenByAddress: state.tokenByAddress, - tokenStateByAddress: state.tokenStateByAddress, + lastForceTokenStateRefetch: state.lastForceTokenStateRefetch, userAddress: state.userAddress, userEtherBalance: state.userEtherBalance, userSuppliedOrderCache: state.userSuppliedOrderCache, diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index 42989e5e1..dea0a8bfe 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -131,43 +131,9 @@ export class Dispatcher { type: ActionTypes.UpdateTokenByAddress, }); } - public updateTokenStateByAddress(tokenStateByAddress: TokenStateByAddress) { + public forceTokenStateRefetch() { this._dispatch({ - data: tokenStateByAddress, - type: ActionTypes.UpdateTokenStateByAddress, - }); - } - public removeFromTokenStateByAddress(tokenAddress: string) { - this._dispatch({ - data: tokenAddress, - type: ActionTypes.RemoveFromTokenStateByAddress, - }); - } - public replaceTokenAllowanceByAddress(address: string, allowance: BigNumber) { - this._dispatch({ - data: { - address, - allowance, - }, - type: ActionTypes.ReplaceTokenAllowanceByAddress, - }); - } - public replaceTokenBalanceByAddress(address: string, balance: BigNumber) { - this._dispatch({ - data: { - address, - balance, - }, - type: ActionTypes.ReplaceTokenBalanceByAddress, - }); - } - public updateTokenBalanceByAddress(address: string, balanceDelta: BigNumber) { - this._dispatch({ - data: { - address, - balanceDelta, - }, - type: ActionTypes.UpdateTokenBalanceByAddress, + type: ActionTypes.ForceTokenStateRefetch, }); } public updateSignatureData(signatureData: SignatureData) { diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 06ac8b670..cee475fa9 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -1,6 +1,7 @@ import { ZeroEx } from '0x.js'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; +import * as moment from 'moment'; import { Action, ActionTypes, @@ -37,7 +38,7 @@ export interface State { shouldBlockchainErrDialogBeOpen: boolean; sideToAssetToken: SideToAssetToken; tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; + lastForceTokenStateRefetch: number; userAddress: string; userEtherBalance: BigNumber; // Note: cache of supplied orderJSON in fill order step. Do not use for anything else. @@ -76,7 +77,7 @@ const INITIAL_STATE: State = { [Side.Receive]: {}, }, tokenByAddress: {}, - tokenStateByAddress: {}, + lastForceTokenStateRefetch: moment().unix(), userAddress: '', userEtherBalance: new BigNumber(0), userSuppliedOrderCache: undefined, @@ -180,74 +181,11 @@ export function reducer(state: State = INITIAL_STATE, action: Action) { }; } - case ActionTypes.UpdateTokenStateByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const updatedTokenStateByAddress = action.data; - _.each(updatedTokenStateByAddress, (tokenState: TokenState, address: string) => { - const updatedTokenState = { - ...tokenStateByAddress[address], - ...tokenState, - }; - tokenStateByAddress[address] = updatedTokenState; - }); - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.RemoveFromTokenStateByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const tokenAddress = action.data; - delete tokenStateByAddress[tokenAddress]; - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.ReplaceTokenAllowanceByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const allowance = action.data.allowance; - const tokenAddress = action.data.address; - tokenStateByAddress[tokenAddress] = { - ...tokenStateByAddress[tokenAddress], - allowance, - }; + case ActionTypes.ForceTokenStateRefetch: return { ...state, - tokenStateByAddress, + lastForceTokenStateRefetch: moment().unix(), }; - } - - case ActionTypes.ReplaceTokenBalanceByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const balance = action.data.balance; - const tokenAddress = action.data.address; - tokenStateByAddress[tokenAddress] = { - ...tokenStateByAddress[tokenAddress], - balance, - }; - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.UpdateTokenBalanceByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const balanceDelta = action.data.balanceDelta; - const tokenAddress = action.data.address; - const currBalance = tokenStateByAddress[tokenAddress].balance; - tokenStateByAddress[tokenAddress] = { - ...tokenStateByAddress[tokenAddress], - balance: currBalance.plus(balanceDelta), - }; - return { - ...state, - tokenStateByAddress, - }; - } case ActionTypes.UpdateOrderSignatureData: { return { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index aec8f7e1e..134d4d7bf 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -125,11 +125,7 @@ export enum ActionTypes { UpdateOrderSignatureData = 'UPDATE_ORDER_SIGNATURE_DATA', UpdateTokenByAddress = 'UPDATE_TOKEN_BY_ADDRESS', RemoveTokenFromTokenByAddress = 'REMOVE_TOKEN_FROM_TOKEN_BY_ADDRESS', - UpdateTokenStateByAddress = 'UPDATE_TOKEN_STATE_BY_ADDRESS', - RemoveFromTokenStateByAddress = 'REMOVE_FROM_TOKEN_STATE_BY_ADDRESS', - ReplaceTokenAllowanceByAddress = 'REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS', - ReplaceTokenBalanceByAddress = 'REPLACE_TOKEN_BALANCE_BY_ADDRESS', - UpdateTokenBalanceByAddress = 'UPDATE_TOKEN_BALANCE_BY_ADDRESS', + ForceTokenStateRefetch = 'FORCE_TOKEN_STATE_REFETCH', UpdateOrderExpiry = 'UPDATE_ORDER_EXPIRY', SwapAssetTokens = 'SWAP_ASSET_TOKENS', UpdateUserAddress = 'UPDATE_USER_ADDRESS', -- cgit From 005a02efeb5ac874e1c1a4dd6679bfa3cc21b1b1 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 28 Jan 2018 17:45:20 +0100 Subject: Fix bug where could not switch to Ledger and back --- packages/website/ts/blockchain.ts | 43 ++++++++++++---------- .../ts/components/inputs/token_amount_input.tsx | 8 ++-- packages/website/ts/redux/dispatcher.ts | 12 ++++++ packages/website/ts/redux/reducer.ts | 19 ++++++++++ packages/website/ts/types.ts | 1 + packages/website/ts/web3_wrapper.ts | 13 +++---- 6 files changed, 65 insertions(+), 31 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 898cb2d01..e0f5a496f 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -37,6 +37,7 @@ import { EtherscanLinkSuffixes, ProviderType, Side, + SideToAssetToken, SignatureData, Token, TokenByAddress, @@ -212,6 +213,7 @@ export class Blockchain { this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); this._zeroEx.setProvider(provider, this.networkId); await this._postInstantiationOrUpdatingProviderZeroExAsync(); + this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceStateAsync(); this._dispatcher.updateProviderType(ProviderType.Ledger); } public async updateProviderToInjectedAsync() { @@ -222,17 +224,18 @@ export class Blockchain { } const provider = this._cachedProvider; this.networkId = this._cachedProviderNetworkId; - this._dispatcher.updateNetworkId(this.networkId); this._web3Wrapper.destroy(); const shouldPollUserAddress = true; this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); - this._dispatcher.updateUserAddress(this._userAddress); + this._zeroEx.setProvider(provider, this.networkId); await this._postInstantiationOrUpdatingProviderZeroExAsync(); + await this.fetchTokenInformationAsync(); + this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceStateAsync(); this._dispatcher.updateProviderType(ProviderType.Injected); delete this._ledgerSubprovider; delete this._cachedProvider; @@ -473,20 +476,10 @@ export class Blockchain { ); this._dispatcher.updateBlockchainIsLoaded(false); - // HACK: Without this timeout, the second call to dispatcher somehow causes blockchainIsLoaded - // to flicker... Need to debug further :(((()))) - await new Promise(resolve => setTimeout(resolve, 100)); this._dispatcher.clearTokenByAddress(); const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); - // HACK: This is a hack so that the loading spinner doesn't show up twice... - // Once for loading the blockchain, another for loading the userAddress - this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); - if (!_.isEmpty(this._userAddress)) { - this._dispatcher.updateUserAddress(this._userAddress); - } - let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId); const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress); if (_.isUndefined(trackedTokensIfExists)) { @@ -507,14 +500,20 @@ export class Blockchain { }); } const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]); - this._dispatcher.updateTokenByAddress(allTokens); - const mostPopularTradingPairTokens: Token[] = [ _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }), _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }), ]; - this._dispatcher.updateChosenAssetTokenAddress(Side.Deposit, mostPopularTradingPairTokens[0].address); - this._dispatcher.updateChosenAssetTokenAddress(Side.Receive, mostPopularTradingPairTokens[1].address); + const sideToAssetToken: SideToAssetToken = { + [Side.Deposit]: { + address: mostPopularTradingPairTokens[0].address, + }, + [Side.Receive]: { + address: mostPopularTradingPairTokens[1].address, + }, + }; + this._dispatcher.batchDispatch(allTokens, this.networkId, this._userAddress, sideToAssetToken); + this._dispatcher.updateBlockchainIsLoaded(true); } private async _showEtherScanLinkAndAwaitTransactionMinedAsync( @@ -699,17 +698,23 @@ export class Blockchain { } const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists); - const networkId = !_.isUndefined(networkIdIfExists) + this.networkId = !_.isUndefined(networkIdIfExists) ? networkIdIfExists : configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET; + this._dispatcher.updateNetworkId(this.networkId); const zeroExConfigs = { - networkId, + networkId: this.networkId, }; this._zeroEx = new ZeroEx(provider, zeroExConfigs); this._updateProviderName(injectedWeb3); const shouldPollUserAddress = true; - this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, networkId, shouldPollUserAddress); + this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); await this._postInstantiationOrUpdatingProviderZeroExAsync(); + this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); + this._dispatcher.updateUserAddress(this._userAddress); + await this.fetchTokenInformationAsync(); + this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + await this._rehydrateStoreWithContractEvents(); } // This method should always be run after instantiating or updating the provider // of the ZeroEx instance. diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index f41d42d02..44f3fc4a8 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -41,7 +41,7 @@ export class TokenAmountInput extends React.Component { + const updatedToken = { + ...tokenByAddress[token.address], + ...token, + }; + tokenByAddress[token.address] = updatedToken; + }); + return { + ...state, + networkId: action.data.networkId, + userAddress: action.data.userAddress, + sideToAssetToken: action.data.sideToAssetToken, + tokenByAddress, + }; + } + case ActionTypes.ForceTokenStateRefetch: return { ...state, diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 134d4d7bf..aeba3a570 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -110,6 +110,7 @@ export enum BalanceErrs { export enum ActionTypes { // Portal + BatchDispatch = 'BATCH_DISPATCH', UpdateScreenWidth = 'UPDATE_SCREEN_WIDTH', UpdateNodeVersion = 'UPDATE_NODE_VERSION', ResetState = 'RESET_STATE', diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index 415df6e8b..e19b0ea06 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -24,9 +24,6 @@ export class Web3Wrapper { this._web3 = new Web3(); this._web3.setProvider(provider); - - // tslint:disable-next-line:no-floating-promises - this._startEmittingNetworkConnectionAndUserBalanceStateAsync(); } public isAddress(address: string) { return this._web3.isAddress(address); @@ -90,11 +87,7 @@ export class Web3Wrapper { public updatePrevUserAddress(userAddress: string) { this._prevUserAddress = userAddress; } - private async _getNetworkAsync() { - const networkId = await promisify(this._web3.version.getNetwork)(); - return networkId; - } - private async _startEmittingNetworkConnectionAndUserBalanceStateAsync() { + public async startEmittingNetworkConnectionAndUserBalanceStateAsync() { if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) { return; // we are already emitting the state } @@ -145,6 +138,10 @@ export class Web3Wrapper { }, ); } + private async _getNetworkAsync() { + const networkId = await promisify(this._web3.version.getNetwork)(); + return networkId; + } private async _updateUserEtherBalanceAsync(userAddress: string) { const balance = await this.getBalanceInEthAsync(userAddress); if (!balance.eq(this._prevUserEtherBalanceInEth)) { -- cgit From 3c3f9ca85b1429821138840b6074503e58fab7e1 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 10:44:30 +0100 Subject: Add network name to the select provider --- .../ts/components/top_bar/provider_picker.tsx | 27 ++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/website/ts/components/top_bar/provider_picker.tsx b/packages/website/ts/components/top_bar/provider_picker.tsx index 418f8696b..4dc8ff0bd 100644 --- a/packages/website/ts/components/top_bar/provider_picker.tsx +++ b/packages/website/ts/components/top_bar/provider_picker.tsx @@ -34,12 +34,6 @@ export class ProviderPicker extends React.Component -
{this.props.injectedProviderName}
- {this._renderNetwork()} -
- ); // Show dropdown with two options return (
@@ -51,29 +45,38 @@ export class ProviderPicker extends React.Component
); } + private _renderLabel(title: string, shouldShowNetwork: boolean) { + const label = ( +
+
{title}
+ {shouldShowNetwork && this._renderNetwork()} +
+ ); + return label; + } private _renderNetwork() { const networkName = constants.NETWORK_NAME_BY_ID[this.props.networkId]; return ( -
-
+
+
-
{networkName}
+
{networkName}
); } -- cgit From d18554e0e88de06fd2196d00cf1469eca1358eaf Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 10:46:15 +0100 Subject: Replace heavy loading animation with simple circular loader --- packages/website/public/gifs/0xAnimation.gif | Bin 909585 -> 0 bytes packages/website/public/images/loading_poster.png | Bin 2505 -> 0 bytes packages/website/public/videos/0xAnimation.mp4 | Bin 970342 -> 0 bytes packages/website/ts/components/portal.tsx | 16 +++++++-- packages/website/ts/components/ui/loading.tsx | 39 ---------------------- 5 files changed, 14 insertions(+), 41 deletions(-) delete mode 100644 packages/website/public/gifs/0xAnimation.gif delete mode 100644 packages/website/public/images/loading_poster.png delete mode 100644 packages/website/public/videos/0xAnimation.mp4 delete mode 100644 packages/website/ts/components/ui/loading.tsx diff --git a/packages/website/public/gifs/0xAnimation.gif b/packages/website/public/gifs/0xAnimation.gif deleted file mode 100644 index b3e32a6ad..000000000 Binary files a/packages/website/public/gifs/0xAnimation.gif and /dev/null differ diff --git a/packages/website/public/images/loading_poster.png b/packages/website/public/images/loading_poster.png deleted file mode 100644 index e5618f260..000000000 Binary files a/packages/website/public/images/loading_poster.png and /dev/null differ diff --git a/packages/website/public/videos/0xAnimation.mp4 b/packages/website/public/videos/0xAnimation.mp4 deleted file mode 100644 index d78c07d4f..000000000 Binary files a/packages/website/public/videos/0xAnimation.mp4 and /dev/null differ diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index e8190e2f5..0182a91f7 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -1,5 +1,6 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; +import CircularProgress from 'material-ui/CircularProgress'; import Paper from 'material-ui/Paper'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; @@ -17,7 +18,6 @@ import { TokenBalances } from 'ts/components/token_balances'; import { TopBar } from 'ts/components/top_bar/top_bar'; import { TradeHistory } from 'ts/components/trade_history/trade_history'; import { FlashMessage } from 'ts/components/ui/flash_message'; -import { Loading } from 'ts/components/ui/loading'; import { GenerateOrderForm } from 'ts/containers/generate_order_form'; import { localStorage } from 'ts/local_storage/local_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; @@ -223,7 +223,19 @@ export class Portal extends React.Component { /> ) : ( - +
+
+
+ +
+
+ Loading Portal... +
+
+
)}
diff --git a/packages/website/ts/components/ui/loading.tsx b/packages/website/ts/components/ui/loading.tsx deleted file mode 100644 index aa319e9e9..000000000 --- a/packages/website/ts/components/ui/loading.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import * as _ from 'lodash'; -import Paper from 'material-ui/Paper'; -import * as React from 'react'; -import { DefaultPlayer as Video } from 'react-html5video'; -import 'react-html5video/dist/styles.css'; -import { utils } from 'ts/utils/utils'; - -interface LoadingProps {} - -interface LoadingState {} - -export class Loading extends React.Component { - public render() { - return ( -
- - {utils.isUserOnMobile() ? ( - - ) : ( -
- -
- )} -
- Connecting to the blockchain... -
-
-
- ); - } -} -- cgit From 45fdfc2d3d2bcfcb37d16f29df2e30524e6d7717 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 10:55:53 +0100 Subject: Use colors module and remove in-lined colors --- packages/website/ts/components/top_bar/provider_display.tsx | 9 ++++++--- packages/website/ts/components/top_bar/provider_picker.tsx | 8 ++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index c7b6a4743..e132f9cee 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -49,11 +49,14 @@ export class ProviderDisplay extends React.Component
-
{providerTitle}
+
{providerTitle}
{displayAddress}
-
- +
+
); diff --git a/packages/website/ts/components/top_bar/provider_picker.tsx b/packages/website/ts/components/top_bar/provider_picker.tsx index 4dc8ff0bd..75261ab94 100644 --- a/packages/website/ts/components/top_bar/provider_picker.tsx +++ b/packages/website/ts/components/top_bar/provider_picker.tsx @@ -9,11 +9,11 @@ import { DropDown } from 'ts/components/ui/drop_down'; import { Identicon } from 'ts/components/ui/identicon'; import { Dispatcher } from 'ts/redux/dispatcher'; import { ProviderType } from 'ts/types'; +import { colors } from 'ts/utils/colors'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; const IDENTICON_DIAMETER = 32; -const SELECTED_BG_COLOR = '#F7F7F7'; interface ProviderPickerProps { networkId: number; @@ -43,12 +43,12 @@ export class ProviderPicker extends React.Component @@ -76,7 +76,7 @@ export class ProviderPicker extends React.Component -
{networkName}
+
{networkName}
); } -- cgit From af08177f79decd8dd3194d300a1fa43d43872229 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 12:10:49 +0100 Subject: Make it such that users can switch between Ledger accounts without first switching back to an injected provider --- packages/website/ts/blockchain.ts | 6 ++++-- .../website/ts/components/dialogs/ledger_config_dialog.tsx | 5 ++++- packages/website/ts/components/top_bar/provider_picker.tsx | 10 ++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index e0f5a496f..71927ef3b 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -190,8 +190,10 @@ export class Blockchain { } // Cache injected provider so that we can switch the user back to it easily - this._cachedProvider = this._web3Wrapper.getProviderObj(); - this._cachedProviderNetworkId = this.networkId; + if (_.isUndefined(this._cachedProvider)) { + this._cachedProvider = this._web3Wrapper.getProviderObj(); + this._cachedProviderNetworkId = this.networkId; + } this._userAddress = ''; this._dispatcher.updateUserAddress(''); // Clear old userAddress diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index a17a51622..66b04f198 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -265,7 +265,10 @@ export class LedgerConfigDialog extends React.Component - + ); } - private _onProviderRadioChanged(e: any, value: string) { + private _onProviderRadioChanged(value: string) { if (value === ProviderType.Ledger) { this.props.onToggleLedgerDialog(); } else { -- cgit From 52394884dad9d4ff7f13891ebba013000ae32484 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 12:11:05 +0100 Subject: Add the stack trace to help with debugging --- packages/website/ts/web3_wrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index e19b0ea06..d3e929917 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -133,7 +133,7 @@ export class Web3Wrapper { }, 5000, (err: Error) => { - utils.consoleLog(`Watching network and balances failed: ${err}`); + utils.consoleLog(`Watching network and balances failed: ${err}, ${err.stack}`); this._stopEmittingNetworkConnectionAndUserBalanceStateAsync(); }, ); -- cgit From 609342be7af3a33ca354bbf9385b1fa187b872d0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 12:45:50 +0100 Subject: Add flash message instructing user to confirm tx on Ledger --- packages/website/ts/blockchain.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 71927ef3b..13c997ba6 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -247,6 +247,7 @@ export class Blockchain { utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + this._showFlashMessageIfLedger(); const txHash = await this._zeroEx.token.setProxyAllowanceAsync( token.address, this._userAddress, @@ -256,6 +257,7 @@ export class Blockchain { const allowance = amountInBaseUnits; } public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise { + this._showFlashMessageIfLedger(); const txHash = await this._zeroEx.token.transferAsync( token.address, this._userAddress, @@ -312,6 +314,7 @@ export class Blockchain { const shouldThrowOnInsufficientBalanceOrAllowance = true; + this._showFlashMessageIfLedger(); const txHash = await this._zeroEx.exchange.fillOrderAsync( signedOrder, fillTakerTokenAmount, @@ -327,6 +330,7 @@ export class Blockchain { return filledTakerTokenAmount; } public async cancelOrderAsync(signedOrder: SignedOrder, cancelTakerTokenAmount: BigNumber): Promise { + this._showFlashMessageIfLedger(); const txHash = await this._zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount); const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); const logs: Array> = receipt.logs as any; @@ -399,6 +403,7 @@ export class Blockchain { if (_.isUndefined(makerAddress)) { throw new Error('Tried to send a sign request but user has no associated addresses'); } + this._showFlashMessageIfLedger(); const ecSignature = await this._zeroEx.signOrderHashAsync(orderHash, makerAddress); const signatureData = _.extend({}, ecSignature, { hash: orderHash, @@ -410,6 +415,7 @@ export class Blockchain { utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); const mintableContract = await this._instantiateContractIfExistsAsync(MintableArtifacts, token.address); + this._showFlashMessageIfLedger(); await mintableContract.mint(constants.MINT_AMOUNT, { from: this._userAddress, }); @@ -423,6 +429,7 @@ export class Blockchain { utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + this._showFlashMessageIfLedger(); const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } @@ -430,6 +437,7 @@ export class Blockchain { utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + this._showFlashMessageIfLedger(); const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } @@ -766,4 +774,9 @@ export class Blockchain { } } } + private _showFlashMessageIfLedger() { + if (!_.isUndefined(this._ledgerSubprovider)) { + this._dispatcher.showFlashMessage('Confirm the transaction on your Ledger Nano S'); + } + } } // tslint:disable:max-file-line-count -- cgit From 63ffa80c5cde2b289043ad58aec6bc2fe4d72993 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 12:46:07 +0100 Subject: Re-set Ledger config dialog to connect step if dialog is closed --- packages/website/ts/components/dialogs/ledger_config_dialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index 66b04f198..7dc1e2620 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -186,6 +186,7 @@ export class LedgerConfigDialog extends React.Component Date: Mon, 29 Jan 2018 13:16:40 +0100 Subject: Add missing entries for Ropsten and Rinkeby testnets, added Ropsten to Ledger network dropdown --- packages/website/ts/blockchain.ts | 4 +-- .../components/dialogs/blockchain_err_dialog.tsx | 4 +-- .../ts/components/dialogs/ledger_config_dialog.tsx | 8 ++++-- packages/website/ts/components/token_balances.tsx | 33 +++++++++++----------- packages/website/ts/utils/configs.ts | 2 ++ packages/website/ts/utils/constants.ts | 4 ++- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 13c997ba6..543d5d8ca 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -112,7 +112,7 @@ export class Blockchain { // injected into their browser. provider = new ProviderEngine(); provider.addProvider(new FilterSubprovider()); - const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET; + const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_KOVAN; provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); provider.start(); } @@ -710,7 +710,7 @@ export class Blockchain { const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists); this.networkId = !_.isUndefined(networkIdIfExists) ? networkIdIfExists - : configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET; + : configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_KOVAN; this._dispatcher.updateNetworkId(this.networkId); const zeroExConfigs = { networkId: this.networkId, diff --git a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx index f555ca6b1..3908d987e 100644 --- a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx +++ b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx @@ -3,7 +3,7 @@ import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; -import { BlockchainErrs } from 'ts/types'; +import { BlockchainErrs, Networks } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -129,7 +129,7 @@ export class BlockchainErrDialog extends React.Component The 0x smart contracts are not deployed on the Ethereum network you are currently connected to (network Id: {this.props.networkId}). In order to use the 0x portal dApp, please connect to the{' '} - {constants.TESTNET_NAME} testnet (network Id: {constants.NETWORK_ID_TESTNET}) + {Networks.kovan} testnet (network Id: {constants.NETWORK_ID_KOVAN}) {configs.IS_MAINNET_ENABLED ? ` or ${constants.MAINNET_NAME} (network Id: ${constants.NETWORK_ID_MAINNET}).` : `.`} diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index 7dc1e2620..510025774 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -10,7 +10,7 @@ import { Blockchain } from 'ts/blockchain'; import { NetworkDropDown } from 'ts/components/dropdowns/network_drop_down'; import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { ProviderType } from 'ts/types'; +import { ProviderType, Networks } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -97,7 +97,11 @@ export class LedgerConfigDialog extends React.Component
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 5b450c772..ecafe5432 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -27,6 +27,7 @@ import { BlockchainCallErrs, BlockchainErrs, EtherscanLinkSuffixes, + Networks, ScreenWidths, Styles, Token, @@ -155,13 +156,13 @@ export class TokenBalances extends React.Component, ]; - const isTestNetwork = this.props.networkId === constants.NETWORK_ID_TESTNET; + const isKovanTestNetwork = this.props.networkId === constants.NETWORK_ID_KOVAN; const dharmaButtonColumnStyle = { paddingLeft: 3, - display: isTestNetwork ? 'table-cell' : 'none', + display: isKovanTestNetwork ? 'table-cell' : 'none', }; const stubColumnStyle = { - display: isTestNetwork ? 'none' : 'table-cell', + display: isKovanTestNetwork ? 'none' : 'table-cell', }; const allTokenRowHeight = _.size(this.props.tokenByAddress) * TOKEN_TABLE_ROW_HEIGHT; const tokenTableHeight = @@ -180,10 +181,10 @@ export class TokenBalances extends React.Component -

{isTestNetwork ? 'Test ether' : 'Ether'}

+

{isKovanTestNetwork ? 'Test ether' : 'Ether'}

- {isTestNetwork + {isKovanTestNetwork ? '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. \ @@ -195,12 +196,12 @@ export class TokenBalances extends React.ComponentCurrency Balance - {isTestNetwork && ( + {isKovanTestNetwork && ( {isSmallScreen ? 'Faucet' : 'Request from faucet'} )} - {isTestNetwork && ( + {isKovanTestNetwork && ( {isSmallScreen ? 'Loan' : 'Request Dharma loan'} @@ -222,7 +223,7 @@ export class TokenBalances extends React.Component - {isTestNetwork && ( + {isKovanTestNetwork && ( )} - {isTestNetwork && ( + {isKovanTestNetwork && (
-

{isTestNetwork ? 'Test tokens' : 'Tokens'}

+

{isKovanTestNetwork ? 'Test tokens' : 'Tokens'}

@@ -261,7 +262,7 @@ export class TokenBalances extends React.Component
- {isTestNetwork + {isKovanTestNetwork ? "Mint some test tokens you'd like to use to generate or fill an order using 0x." : "Set trading permissions for a token you'd like to start trading."}
@@ -391,7 +392,7 @@ export class TokenBalances extends React.Component )} {token.symbol === ZRX_TOKEN_SYMBOL && - this.props.networkId === constants.NETWORK_ID_TESTNET && ( + this.props.networkId === constants.NETWORK_ID_KOVAN && ( - Our faucet can only send test Ether to addresses on the {constants.TESTNET_NAME} testnet - (networkId {constants.NETWORK_ID_TESTNET}). Please make sure you are connected to the{' '} - {constants.TESTNET_NAME} testnet and try requesting ether again. + Our faucet can only send test Ether to addresses on the {Networks.kovan} testnet (networkId{' '} + {constants.NETWORK_ID_KOVAN}). Please make sure you are connected to the {Networks.kovan}{' '} + testnet and try requesting ether again.
); @@ -568,7 +569,7 @@ export class TokenBalances extends React.Component Date: Mon, 29 Jan 2018 13:25:51 +0100 Subject: Fix bug related to balance/allowance fetching being async --- packages/website/ts/components/token_balances.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index ecafe5432..776ccb16e 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -133,6 +133,20 @@ export class TokenBalances extends React.Component token.address); + // Add placeholder entry for this token to the state, since fetching the + // balance/allowance is asynchronous + const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + for (const tokenAddress of newTokenAddresses) { + trackedTokenStateByAddress[tokenAddress] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + isLoaded: false, + }; + } + this.setState({ + trackedTokenStateByAddress, + }); + // Fetch the actual balance/allowance. this._fetchBalancesAndAllowancesAsync(newTokenAddresses); } } -- cgit From 8ccdd54974a43dc44d3aa7475442c7e2a1a6441a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 13:57:05 +0100 Subject: Small improvements --- packages/website/ts/web3_wrapper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index d3e929917..00b18a266 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -120,7 +120,7 @@ export class Web3Wrapper { } // Check for user ether balance changes - if (userAddressIfExists !== '') { + if (!_.isEmpty(userAddressIfExists)) { await this._updateUserEtherBalanceAsync(userAddressIfExists); } } else { @@ -133,7 +133,7 @@ export class Web3Wrapper { }, 5000, (err: Error) => { - utils.consoleLog(`Watching network and balances failed: ${err}, ${err.stack}`); + utils.consoleLog(`Watching network and balances failed: ${err.stack}`); this._stopEmittingNetworkConnectionAndUserBalanceStateAsync(); }, ); -- cgit From fa6130c90787c6ce20a610693b6675e06f93365b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 14:10:57 +0100 Subject: remove unused type --- packages/website/ts/types.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index aeba3a570..7b7b80a5d 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -493,16 +493,6 @@ export interface SignPersonalMessageParams { data: string; } -export interface TxParams { - nonce: string; - gasPrice?: number; - gasLimit: string; - to: string; - value?: string; - data?: string; - chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3 -} - export interface PublicNodeUrlsByNetworkId { [networkId: number]: string[]; } -- cgit From 72571628dacfca91b936f15ad3832820b0bb159a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 16:38:05 +0100 Subject: Fetch default gasPrice from our ethGasStation API mirror and set it for all transactions --- packages/website/ts/blockchain.ts | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 543d5d8ca..5d71a14c8 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -67,6 +67,7 @@ export class Blockchain { private _cachedProvider: Web3.Provider; private _cachedProviderNetworkId: number; private _ledgerSubprovider: LedgerWalletSubprovider; + private _defaultGasPrice: BigNumber; private static async _onPageLoadAsync(): Promise { if (document.readyState === 'complete') { return; // Already loaded @@ -122,6 +123,9 @@ export class Blockchain { constructor(dispatcher: Dispatcher, isSalePage: boolean = false) { this._dispatcher = dispatcher; this._userAddress = ''; + this._defaultGasPrice = new BigNumber(30000000000); + // tslint:disable-next-line:no-floating-promises + this._updateDefaultGasPriceAsync(); // tslint:disable-next-line:no-floating-promises this._onPageLoadInitFireAndForgetAsync(); } @@ -252,6 +256,9 @@ export class Blockchain { token.address, this._userAddress, amountInBaseUnits, + { + gasPrice: this._defaultGasPrice, + }, ); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); const allowance = amountInBaseUnits; @@ -263,6 +270,9 @@ export class Blockchain { this._userAddress, toAddress, amountInBaseUnits, + { + gasPrice: this._defaultGasPrice, + }, ); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx); @@ -320,6 +330,9 @@ export class Blockchain { fillTakerTokenAmount, shouldThrowOnInsufficientBalanceOrAllowance, this._userAddress, + { + gasPrice: this._defaultGasPrice, + }, ); const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); const logs: Array> = receipt.logs as any; @@ -331,7 +344,9 @@ export class Blockchain { } public async cancelOrderAsync(signedOrder: SignedOrder, cancelTakerTokenAmount: BigNumber): Promise { this._showFlashMessageIfLedger(); - const txHash = await this._zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount); + const txHash = await this._zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount, { + gasPrice: this._defaultGasPrice, + }); const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); const logs: Array> = receipt.logs as any; this._zeroEx.exchange.throwLogErrorsAsErrors(logs); @@ -418,6 +433,7 @@ export class Blockchain { this._showFlashMessageIfLedger(); await mintableContract.mint(constants.MINT_AMOUNT, { from: this._userAddress, + gasPrice: this._defaultGasPrice, }); const balanceDelta = constants.MINT_AMOUNT; } @@ -430,7 +446,9 @@ export class Blockchain { utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); this._showFlashMessageIfLedger(); - const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress); + const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress, { + gasPrice: this._defaultGasPrice, + }); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise { @@ -438,7 +456,9 @@ export class Blockchain { utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); this._showFlashMessageIfLedger(); - const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress); + const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress, { + gasPrice: this._defaultGasPrice, + }); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } public async doesContractExistAtAddressAsync(address: string) { @@ -779,4 +799,15 @@ export class Blockchain { this._dispatcher.showFlashMessage('Confirm the transaction on your Ledger Nano S'); } } + private async _updateDefaultGasPriceAsync() { + const endpoint = `${configs.BACKEND_BASE_URL}/eth_gas_station`; + const response = await fetch(endpoint); + if (response.status !== 200) { + return; // noop and we keep hard-coded default + } + const gasInfo = await response.json(); + const gasPriceInGwei = new BigNumber(gasInfo.average / 10); + const gasPriceInWei = gasPriceInGwei.mul(1000000000); + this._defaultGasPrice = gasPriceInWei; + } } // tslint:disable:max-file-line-count -- cgit From 5c2d725721407878ffda936b6e6ee3ff97b80434 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 16:38:30 +0100 Subject: Use live backend on development --- packages/website/ts/utils/configs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index 1e46be0be..34360cf43 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -16,7 +16,7 @@ const isDevelopment = _.includes( const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs'; export const configs = { - BACKEND_BASE_URL: isDevelopment ? 'https://localhost:3001' : 'https://website-api.0xproject.com', + BACKEND_BASE_URL: 'https://website-api.0xproject.com', BASE_URL, BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', CONTRACT_ADDRESS: { -- cgit From 8175c7c08575f864578d150bc9ae0800f5eaf037 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 17:56:35 +0100 Subject: Remove the ability to clear tokenByAddress. It should simply be updated. --- packages/website/ts/blockchain.ts | 1 - packages/website/ts/redux/dispatcher.ts | 5 ----- packages/website/ts/redux/reducer.ts | 7 ------- packages/website/ts/types.ts | 1 - 4 files changed, 14 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 5d71a14c8..fd8d536ed 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -506,7 +506,6 @@ export class Blockchain { ); this._dispatcher.updateBlockchainIsLoaded(false); - this._dispatcher.clearTokenByAddress(); const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index c7e0a5af3..9df18e54c 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -121,11 +121,6 @@ export class Dispatcher { type: ActionTypes.RemoveTokenFromTokenByAddress, }); } - public clearTokenByAddress() { - this._dispatch({ - type: ActionTypes.ClearTokenByAddress, - }); - } public batchDispatch(tokens: Token[], networkId: number, userAddress: string, sideToAssetToken: SideToAssetToken) { this._dispatch({ data: { diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 427a8dd4a..f94da003f 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -140,13 +140,6 @@ export function reducer(state: State = INITIAL_STATE, action: Action) { }; } - case ActionTypes.ClearTokenByAddress: { - return { - ...state, - tokenByAddress: {}, - }; - } - case ActionTypes.AddTokenToTokenByAddress: { const newTokenByAddress = state.tokenByAddress; newTokenByAddress[action.data.address] = action.data; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 7b7b80a5d..1c14c1435 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -116,7 +116,6 @@ export enum ActionTypes { ResetState = 'RESET_STATE', AddTokenToTokenByAddress = 'ADD_TOKEN_TO_TOKEN_BY_ADDRESS', BlockchainErrEncountered = 'BLOCKCHAIN_ERR_ENCOUNTERED', - ClearTokenByAddress = 'CLEAR_TOKEN_BY_ADDRESS', UpdateBlockchainIsLoaded = 'UPDATE_BLOCKCHAIN_IS_LOADED', UpdateNetworkId = 'UPDATE_NETWORK_ID', UpdateChosenAssetToken = 'UPDATE_CHOSEN_ASSET_TOKEN', -- cgit From bb0cedd2de44a0b6533df37133ae6f576ee84b2a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 29 Jan 2018 18:35:06 +0100 Subject: Disallow negative amounts --- packages/website/ts/components/inputs/balance_bounded_input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx index 67d5b781e..3bbc7a5f6 100644 --- a/packages/website/ts/components/inputs/balance_bounded_input.tsx +++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx @@ -103,7 +103,7 @@ export class BalanceBoundedInput extends React.Component { const isValid = _.isUndefined(errMsg); - if (utils.isNumeric(amountString)) { + if (utils.isNumeric(amountString) && !_.includes(amountString, '-')) { this.props.onChange(isValid, new BigNumber(amountString)); } else { this.props.onChange(isValid); -- cgit From c0facfc28f65495a52f9405613fe4a08aff5d164 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 11:15:36 +0100 Subject: Call destroy ealier so that web3Wrapper stops polling for userAddress/networkId updates before we prep for the batchDispatch --- packages/website/ts/blockchain.ts | 7 +++++-- packages/website/ts/web3_wrapper.ts | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index fd8d536ed..aba438e8d 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -199,6 +199,8 @@ export class Blockchain { this._cachedProviderNetworkId = this.networkId; } + this._web3Wrapper.destroy(); + this._userAddress = ''; this._dispatcher.updateUserAddress(''); // Clear old userAddress @@ -212,7 +214,6 @@ export class Blockchain { provider.addProvider(new FilterSubprovider()); provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); provider.start(); - this._web3Wrapper.destroy(); this.networkId = networkId; this._dispatcher.updateNetworkId(this.networkId); const shouldPollUserAddress = false; @@ -228,10 +229,12 @@ export class Blockchain { if (_.isUndefined(this._cachedProvider)) { return; // Going from injected to injected, so we noop } + + this._web3Wrapper.destroy(); + const provider = this._cachedProvider; this.networkId = this._cachedProviderNetworkId; - this._web3Wrapper.destroy(); const shouldPollUserAddress = true; this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index 00b18a266..1e2f8ff5d 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -150,6 +150,8 @@ export class Web3Wrapper { } } private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() { - intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId); + if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) { + intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId); + } } } -- cgit From 86cc011212088801a778d947ae925cc0b1ddadf8 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 11:16:13 +0100 Subject: Wholesale replace the tokenByAddress and de-dup properly --- packages/website/ts/blockchain.ts | 24 +++++++++++++--------- .../ts/local_storage/tracked_token_storage.ts | 12 +++++++---- packages/website/ts/redux/dispatcher.ts | 10 +++++++-- packages/website/ts/redux/reducer.ts | 11 +--------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index aba438e8d..d310464ed 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -512,26 +512,30 @@ export class Blockchain { const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); - let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId); + const trackedTokensByAddress = trackedTokenStorage.getTrackedTokensByAddress(this._userAddress, this.networkId); const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress); - if (_.isUndefined(trackedTokensIfExists)) { - trackedTokensIfExists = _.map(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => { + if (_.isEmpty(trackedTokensByAddress)) { + _.each(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => { const token = _.find(tokenRegistryTokens, t => t.symbol === symbol); token.isTracked = true; - return token; + trackedTokensByAddress[token.address] = token; }); - _.each(trackedTokensIfExists, token => { + _.each(trackedTokensByAddress, (token: Token, address: string) => { trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token); }); } else { // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array - _.each(trackedTokensIfExists, trackedToken => { - if (!_.isUndefined(tokenRegistryTokensByAddress[trackedToken.address])) { - tokenRegistryTokensByAddress[trackedToken.address].isTracked = true; + _.each(trackedTokensByAddress, (trackedToken: Token, address: string) => { + if (!_.isUndefined(tokenRegistryTokensByAddress[address])) { + tokenRegistryTokensByAddress[address].isTracked = true; } }); } - const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]); + const allTokensByAddress = { + ...tokenRegistryTokensByAddress, + ...trackedTokensByAddress, + }; + const allTokens = _.values(allTokensByAddress); const mostPopularTradingPairTokens: Token[] = [ _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }), _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }), @@ -544,7 +548,7 @@ export class Blockchain { address: mostPopularTradingPairTokens[1].address, }, }; - this._dispatcher.batchDispatch(allTokens, this.networkId, this._userAddress, sideToAssetToken); + this._dispatcher.batchDispatch(allTokensByAddress, this.networkId, this._userAddress, sideToAssetToken); this._dispatcher.updateBlockchainIsLoaded(true); } diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts index 7733e8436..f92dd6298 100644 --- a/packages/website/ts/local_storage/tracked_token_storage.ts +++ b/packages/website/ts/local_storage/tracked_token_storage.ts @@ -1,6 +1,6 @@ import * as _ from 'lodash'; import { localStorage } from 'ts/local_storage/local_storage'; -import { Token, TrackedTokensByUserAddress } from 'ts/types'; +import { Token, TokenByAddress, TrackedTokensByUserAddress } from 'ts/types'; import { configs } from 'ts/utils/configs'; const TRACKED_TOKENS_KEY = 'trackedTokens'; @@ -39,10 +39,11 @@ export const trackedTokenStorage = { const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString); return trackedTokensByUserAddress; }, - getTrackedTokensIfExists(userAddress: string, networkId: number): Token[] { + getTrackedTokensByAddress(userAddress: string, networkId: number): TokenByAddress { + const trackedTokensByAddress: TokenByAddress = {}; const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY); if (_.isEmpty(trackedTokensJSONString)) { - return undefined; + return trackedTokensByAddress; } const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString); const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; @@ -50,7 +51,10 @@ export const trackedTokenStorage = { return undefined; } const trackedTokens = trackedTokensByNetworkId[networkId]; - return trackedTokens; + _.each(trackedTokens, (trackedToken: Token) => { + trackedTokensByAddress[trackedToken.address] = trackedToken; + }); + return trackedTokensByAddress; }, removeTrackedToken(userAddress: string, networkId: number, tokenAddress: string): void { const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index 9df18e54c..aae7683ff 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -12,6 +12,7 @@ import { SideToAssetToken, SignatureData, Token, + TokenByAddress, TokenStateByAddress, } from 'ts/types'; @@ -121,10 +122,15 @@ export class Dispatcher { type: ActionTypes.RemoveTokenFromTokenByAddress, }); } - public batchDispatch(tokens: Token[], networkId: number, userAddress: string, sideToAssetToken: SideToAssetToken) { + public batchDispatch( + tokenByAddress: TokenByAddress, + networkId: number, + userAddress: string, + sideToAssetToken: SideToAssetToken, + ) { this._dispatch({ data: { - tokens, + tokenByAddress, networkId, userAddress, sideToAssetToken, diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index f94da003f..4b5a9138f 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -175,21 +175,12 @@ export function reducer(state: State = INITIAL_STATE, action: Action) { } case ActionTypes.BatchDispatch: { - const tokenByAddress = state.tokenByAddress; - const tokens = action.data.tokens; - _.each(tokens, token => { - const updatedToken = { - ...tokenByAddress[token.address], - ...token, - }; - tokenByAddress[token.address] = updatedToken; - }); return { ...state, networkId: action.data.networkId, userAddress: action.data.userAddress, sideToAssetToken: action.data.sideToAssetToken, - tokenByAddress, + tokenByAddress: action.data.tokenByAddress, }; } -- cgit From 8d30058a6dade197efbfa24380a479d7b72a882a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 14:19:58 +0100 Subject: Pass in whether we want the personal message prefix appended and never append it for Ledger. This fixes signing when Ledger is used and the backing node is not Parity --- packages/website/ts/blockchain.ts | 15 ++++++++++++++- packages/website/ts/utils/utils.ts | 6 ++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index d310464ed..d7c23205d 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -421,8 +421,21 @@ export class Blockchain { if (_.isUndefined(makerAddress)) { throw new Error('Tried to send a sign request but user has no associated addresses'); } + this._showFlashMessageIfLedger(); - const ecSignature = await this._zeroEx.signOrderHashAsync(orderHash, makerAddress); + const nodeVersion = await this._web3Wrapper.getNodeVersionAsync(); + const isParityNode = utils.isParityNode(nodeVersion); + const isTestRpc = utils.isTestRpc(nodeVersion); + const isLedgerSigner = !_.isUndefined(this._ledgerSubprovider); + let shouldAddPersonalMessagePrefix = true; + if ((isParityNode && !isLedgerSigner) || isTestRpc || isLedgerSigner) { + shouldAddPersonalMessagePrefix = false; + } + const ecSignature = await this._zeroEx.signOrderHashAsync( + orderHash, + makerAddress, + shouldAddPersonalMessagePrefix, + ); const signatureData = _.extend({}, ecSignature, { hash: orderHash, }); diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 23db8c0a7..003ecd30a 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -276,4 +276,10 @@ export const utils = { exchangeContractErrorToHumanReadableError[error] || ZeroExErrorToHumanReadableError[error]; return humanReadableErrorMsg; }, + isParityNode(nodeVersion: string): boolean { + return _.includes(nodeVersion, 'Parity'); + }, + isTestRpc(nodeVersion: string): boolean { + return _.includes(nodeVersion, 'TestRPC'); + }, }; -- cgit From 89e98240b485bfdfc62d27dc2982dcfab9169ea9 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 15:05:55 +0100 Subject: Add Rinkeby support --- packages/website/public/images/network_icons/rinkby.png | Bin 126 -> 0 bytes packages/website/public/images/network_icons/rinkeby.png | Bin 0 -> 126 bytes .../ts/components/dialogs/ledger_config_dialog.tsx | 9 +++------ 3 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 packages/website/public/images/network_icons/rinkby.png create mode 100644 packages/website/public/images/network_icons/rinkeby.png diff --git a/packages/website/public/images/network_icons/rinkby.png b/packages/website/public/images/network_icons/rinkby.png deleted file mode 100644 index f9ba18778..000000000 Binary files a/packages/website/public/images/network_icons/rinkby.png and /dev/null differ diff --git a/packages/website/public/images/network_icons/rinkeby.png b/packages/website/public/images/network_icons/rinkeby.png new file mode 100644 index 000000000..f9ba18778 Binary files /dev/null and b/packages/website/public/images/network_icons/rinkeby.png differ diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index 510025774..d5510606b 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -10,7 +10,7 @@ import { Blockchain } from 'ts/blockchain'; import { NetworkDropDown } from 'ts/components/dropdowns/network_drop_down'; import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { ProviderType, Networks } from 'ts/types'; +import { Networks, ProviderType } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -79,6 +79,7 @@ export class LedgerConfigDialog extends React.Component
Follow these instructions before proceeding:
@@ -97,11 +98,7 @@ export class LedgerConfigDialog extends React.Component
-- cgit From da1071526f1faae9e80b992bcbd3dfd3c9a98e1a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 19:03:10 +0100 Subject: Add browser data to dialog info --- packages/website/ts/components/dialogs/ledger_config_dialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index d5510606b..d9b4d1504 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -85,7 +85,7 @@ export class LedgerConfigDialog extends React.ComponentFollow these instructions before proceeding:
  1. Connect your Ledger Nano S & Open the Ethereum application
  2. -
  3. Verify that Browser Support is enabled in Settings
  4. +
  5. Verify that "Browser Support" AND "Contract Data" are enabled in Settings
  6. If no Browser Support is found in settings, verify that you have{' '} -- cgit From 5019c5194044f9ab2631090365e09083044a730d Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 20:10:51 +0100 Subject: Default the derivation path to that found in the Ledger subprovider --- packages/website/ts/components/dialogs/ledger_config_dialog.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index d9b4d1504..5b7e20671 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -45,12 +45,15 @@ interface LedgerConfigDialogState { export class LedgerConfigDialog extends React.Component { constructor(props: LedgerConfigDialogProps) { super(props); + const derivationPathIfExists = props.blockchain.getLedgerDerivationPathIfExists(); this.state = { connectionErrMsg: '', stepIndex: LedgerSteps.CONNECT, userAddresses: [], addressBalances: [], - derivationPath: configs.DEFAULT_DERIVATION_PATH, + derivationPath: _.isUndefined(derivationPathIfExists) + ? configs.DEFAULT_DERIVATION_PATH + : derivationPathIfExists, derivationErrMsg: '', preferredNetworkId: props.networkId, }; -- cgit From 144a507a2e0e341e8c8b97f67a25e1283ebc3687 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 20:11:18 +0100 Subject: Fix bug where we were return undefined instead of the empty object --- packages/website/ts/local_storage/tracked_token_storage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts index f92dd6298..f865f8109 100644 --- a/packages/website/ts/local_storage/tracked_token_storage.ts +++ b/packages/website/ts/local_storage/tracked_token_storage.ts @@ -48,7 +48,7 @@ export const trackedTokenStorage = { const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString); const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; if (_.isUndefined(trackedTokensByNetworkId)) { - return undefined; + return trackedTokensByAddress; } const trackedTokens = trackedTokensByNetworkId[networkId]; _.each(trackedTokens, (trackedToken: Token) => { -- cgit From e219772b2a25712f41fb819be36917d3b889201f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 20:12:32 +0100 Subject: Fix all setState calls after unmounted errors. Decided to create our own flag rather then using a cancellablePromise since there was little to be gained from this alternative --- .../dialogs/eth_weth_conversion_dialog.tsx | 15 +++++++++---- packages/website/ts/components/eth_wrappers.tsx | 25 ++++++++++++++-------- packages/website/ts/components/fill_order.tsx | 19 ++++++++++------ .../ts/components/inputs/token_amount_input.tsx | 17 ++++++++++----- packages/website/ts/components/token_balances.tsx | 13 ++++++++--- .../ts/pages/documentation/documentation.tsx | 23 +++++++++++++------- packages/website/ts/pages/wiki/wiki.tsx | 21 +++++++++++------- 7 files changed, 90 insertions(+), 43 deletions(-) diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx index a3a39a1b9..acd4a7110 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -33,8 +33,10 @@ export class EthWethConversionDialog extends React.Component< EthWethConversionDialogProps, EthWethConversionDialogState > { + private _isUnmounted: boolean; constructor() { super(); + this._isUnmounted = false; this.state = { shouldShowIncompleteErrs: false, hasErrors: false, @@ -46,6 +48,9 @@ export class EthWethConversionDialog extends React.Component< // tslint:disable-next-line:no-floating-promises this._fetchEthTokenBalanceAsync(); } + public componentWillUnmount() { + this._isUnmounted = true; + } public render() { const convertDialogActions = [ , @@ -181,9 +186,11 @@ export class EthWethConversionDialog extends React.Component< this.props.userAddress, this.props.token.address, ); - this.setState({ - isEthTokenBalanceLoaded: true, - ethTokenBalance: balance, - }); + if (!this._isUnmounted) { + this.setState({ + isEthTokenBalanceLoaded: true, + ethTokenBalance: balance, + }); + } } } diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 460a6cae3..90d2c0514 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -54,8 +54,10 @@ interface EthWrappersState { } export class EthWrappers extends React.Component { + private _isUnmounted: boolean; constructor(props: EthWrappersProps) { super(props); + this._isUnmounted = false; const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; @@ -91,6 +93,9 @@ export class EthWrappers extends React.Component { private _validator: SchemaValidator; + private _isUnmounted: boolean; constructor(props: FillOrderProps) { super(props); + this._isUnmounted = false; this.state = { globalErrMsg: '', didOrderValidationRun: false, @@ -90,6 +92,9 @@ export class FillOrder extends React.Component { public componentDidMount() { window.scrollTo(0, 0); } + public componentWillUnmount() { + this._isUnmounted = true; + } public render() { return (
    @@ -456,12 +461,14 @@ export class FillOrder extends React.Component { if (!_.isEmpty(orderJSON)) { orderJSONErrMsg = 'Submitted order JSON is not valid JSON'; } - this.setState({ - didOrderValidationRun: true, - orderJSON, - orderJSONErrMsg, - parsedOrder, - }); + if (!this._isUnmounted) { + this.setState({ + didOrderValidationRun: true, + orderJSON, + orderJSONErrMsg, + parsedOrder, + }); + } return; } diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index 44f3fc4a8..9078f7fe1 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -30,8 +30,10 @@ interface TokenAmountInputState { } export class TokenAmountInput extends React.Component { + private _isUnmounted: boolean; constructor(props: TokenAmountInputProps) { super(props); + this._isUnmounted = false; const defaultAmount = new BigNumber(0); this.state = { balance: defaultAmount, @@ -43,6 +45,9 @@ export class TokenAmountInput extends React.Component { + private _isUnmounted: boolean; public constructor(props: TokenBalancesProps) { super(props); + this._isUnmounted = false; const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens); this.state = { errorType: undefined, @@ -109,6 +111,9 @@ export class TokenBalances extends React.Component { + private _isUnmounted: boolean; constructor(props: DocumentationAllProps) { super(props); + this._isUnmounted = false; this.state = { docAgnosticFormat: undefined, }; @@ -92,6 +94,9 @@ export class Documentation extends React.Component { - this._scrollToHash(); - }, - ); + if (!this._isUnmounted) { + this.setState( + { + docAgnosticFormat, + }, + () => { + this._scrollToHash(); + }, + ); + } } } diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index a3cf72450..daf5c27a7 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -45,8 +45,10 @@ const styles: Styles = { export class Wiki extends React.Component { private _wikiBackoffTimeoutId: number; + private _isUnmounted: boolean; constructor(props: WikiProps) { super(props); + this._isUnmounted = false; this.state = { articlesBySection: undefined, }; @@ -56,6 +58,7 @@ export class Wiki extends React.Component { this._fetchArticlesBySectionAsync(); } public componentWillUnmount() { + this._isUnmounted = true; clearTimeout(this._wikiBackoffTimeoutId); } public render() { @@ -179,14 +182,16 @@ export class Wiki extends React.Component { return; } const articlesBySection = await response.json(); - this.setState( - { - articlesBySection, - }, - () => { - this._scrollToHash(); - }, - ); + if (!this._isUnmounted) { + this.setState( + { + articlesBySection, + }, + () => { + this._scrollToHash(); + }, + ); + } } private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) { const sectionNames = _.keys(articlesBySection); -- cgit From 57acb8db5c48df994f106f8e4b369f7e780f7c9c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 20:28:15 +0100 Subject: Shrink img --- .../website/public/images/metamask_or_parity.png | Bin 48180 -> 22907 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/website/public/images/metamask_or_parity.png b/packages/website/public/images/metamask_or_parity.png index 1085a0120..fda646558 100644 Binary files a/packages/website/public/images/metamask_or_parity.png and b/packages/website/public/images/metamask_or_parity.png differ -- cgit From 16ea0348a9776c99bf50013fca65deb2ba2d698e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 20:45:09 +0100 Subject: Fix linter errors --- packages/website/ts/blockchain.ts | 9 +++------ .../website/ts/components/dialogs/ledger_config_dialog.tsx | 2 +- packages/website/ts/components/eth_weth_conversion_button.tsx | 6 ++---- packages/website/ts/components/eth_wrappers.tsx | 3 --- .../website/ts/components/generate_order/asset_picker.tsx | 2 +- .../ts/components/generate_order/generate_order_form.tsx | 1 - .../website/ts/components/generate_order/new_token_form.tsx | 3 +-- packages/website/ts/components/inputs/token_amount_input.tsx | 3 ++- packages/website/ts/components/portal.tsx | 3 --- packages/website/ts/components/send_button.tsx | 6 ++---- packages/website/ts/components/token_balances.tsx | 4 ++-- packages/website/ts/components/top_bar/provider_display.tsx | 4 ---- packages/website/ts/components/top_bar/provider_picker.tsx | 8 -------- packages/website/ts/components/top_bar/top_bar.tsx | 5 ++--- packages/website/ts/components/ui/drop_down.tsx | 4 +--- packages/website/ts/containers/generate_order_form.tsx | 9 +-------- packages/website/ts/containers/portal.tsx | 11 +---------- packages/website/ts/redux/dispatcher.ts | 1 - packages/website/ts/redux/reducer.ts | 2 -- packages/website/ts/types.ts | 4 ---- packages/website/ts/web3_wrapper.ts | 2 +- 21 files changed, 20 insertions(+), 72 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index d7c23205d..38d405033 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -41,7 +41,6 @@ import { SignatureData, Token, TokenByAddress, - TokenStateByAddress, } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -220,7 +219,7 @@ export class Blockchain { this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); this._zeroEx.setProvider(provider, this.networkId); await this._postInstantiationOrUpdatingProviderZeroExAsync(); - this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceState(); this._dispatcher.updateProviderType(ProviderType.Ledger); } public async updateProviderToInjectedAsync() { @@ -244,7 +243,7 @@ export class Blockchain { await this._postInstantiationOrUpdatingProviderZeroExAsync(); await this.fetchTokenInformationAsync(); - this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceState(); this._dispatcher.updateProviderType(ProviderType.Injected); delete this._ledgerSubprovider; delete this._cachedProvider; @@ -264,7 +263,6 @@ export class Blockchain { }, ); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const allowance = amountInBaseUnits; } public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise { this._showFlashMessageIfLedger(); @@ -451,7 +449,6 @@ export class Blockchain { from: this._userAddress, gasPrice: this._defaultGasPrice, }); - const balanceDelta = constants.MINT_AMOUNT; } public async getBalanceInEthAsync(owner: string): Promise { const balance = await this._web3Wrapper.getBalanceInEthAsync(owner); @@ -762,7 +759,7 @@ export class Blockchain { this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); this._dispatcher.updateUserAddress(this._userAddress); await this.fetchTokenInformationAsync(); - this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceState(); await this._rehydrateStoreWithContractEvents(); } // This method should always be run after instantiating or updating the provider diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index 5b7e20671..bc5f05241 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -10,7 +10,7 @@ import { Blockchain } from 'ts/blockchain'; import { NetworkDropDown } from 'ts/components/dropdowns/network_drop_down'; import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { Networks, ProviderType } from 'ts/types'; +import { ProviderType } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index 52240fd0f..62bafdba7 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; import { EthWethConversionDialog } from 'ts/components/dialogs/eth_weth_conversion_dialog'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { BlockchainCallErrs, Side, Token, TokenState } from 'ts/types'; +import { BlockchainCallErrs, Side, Token } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; @@ -16,7 +16,6 @@ interface EthWethConversionButtonProps { networkId: number; direction: Side; ethToken: Token; - ethTokenState: TokenState; dispatcher: Dispatcher; blockchain: Blockchain; userEtherBalance: BigNumber; @@ -93,7 +92,6 @@ export class EthWethConversionButton extends React.Component< }); this._toggleConversionDialog(); const token = this.props.ethToken; - const tokenState = this.props.ethTokenState; try { if (direction === Side.Deposit) { await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value); @@ -105,7 +103,7 @@ export class EthWethConversionButton extends React.Component< this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`); } if (!this.props.isOutdatedWrappedEther) { - this.props.refetchEthTokenStateAsync(); + await this.props.refetchEthTokenStateAsync(); } this.props.onConversionSuccessful(); } catch (err) { diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 90d2c0514..db1ceecb5 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -16,7 +16,6 @@ import { Token, TokenByAddress, TokenState, - TokenStateByAddress, } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; @@ -163,7 +162,6 @@ export class EthWrappers extends React.Component void; @@ -69,10 +68,9 @@ export class SendButton extends React.Component {
    )} {this.props.blockchainIsLoaded && ( -
    +
    Date: Tue, 30 Jan 2018 20:53:09 +0100 Subject: Fix prettier mess --- packages/website/package.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/website/package.json b/packages/website/package.json index 3d6cc24f7..e5c381150 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -8,12 +8,9 @@ "clean": "shx rm -f public/bundle*", "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", "dev": "webpack-dev-server --content-base public --https", - "update_contracts": - "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;", - "deploy_staging": - "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", - "deploy_live": - "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers" + "update_contracts": "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;", + "deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", + "deploy_live": "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers" }, "config": { "artifacts": "Mintable" -- cgit From adc6170f029cdcee7c3197b60e579e87af402d1e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 20:53:22 +0100 Subject: Make default gasPrice more readable --- packages/website/ts/blockchain.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 38d405033..d53994c0c 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -54,6 +54,7 @@ import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); import * as MintableArtifacts from '../contracts/Mintable.json'; const BLOCK_NUMBER_BACK_TRACK = 50; +const GWEI_IN_WEI = 1000000000; export class Blockchain { public networkId: number; @@ -122,7 +123,8 @@ export class Blockchain { constructor(dispatcher: Dispatcher, isSalePage: boolean = false) { this._dispatcher = dispatcher; this._userAddress = ''; - this._defaultGasPrice = new BigNumber(30000000000); + const defaultGasPrice = GWEI_IN_WEI * 30; + this._defaultGasPrice = new BigNumber(defaultGasPrice); // tslint:disable-next-line:no-floating-promises this._updateDefaultGasPriceAsync(); // tslint:disable-next-line:no-floating-promises -- cgit From ecf86d1d1330ae99194b94cce9167f5d42228a6d Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 21:01:16 +0100 Subject: Uppercase Networks enum values --- .../ts/components/dialogs/blockchain_err_dialog.tsx | 2 +- packages/website/ts/components/token_balances.tsx | 4 ++-- .../website/ts/pages/documentation/documentation.tsx | 6 +++--- packages/website/ts/types.ts | 8 ++++---- packages/website/ts/utils/configs.ts | 6 +++--- packages/website/ts/utils/constants.ts | 16 ++++++++-------- packages/website/ts/utils/utils.ts | 2 +- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx index 3908d987e..278e2bbf5 100644 --- a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx +++ b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx @@ -129,7 +129,7 @@ export class BlockchainErrDialog extends React.Component The 0x smart contracts are not deployed on the Ethereum network you are currently connected to (network Id: {this.props.networkId}). In order to use the 0x portal dApp, please connect to the{' '} - {Networks.kovan} testnet (network Id: {constants.NETWORK_ID_KOVAN}) + {Networks.Kovan} testnet (network Id: {constants.NETWORK_ID_KOVAN}) {configs.IS_MAINNET_ENABLED ? ` or ${constants.MAINNET_NAME} (network Id: ${constants.NETWORK_ID_MAINNET}).` : `.`} diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 9b9304e23..c6a9a46be 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -497,8 +497,8 @@ export class TokenBalances extends React.Component - Our faucet can only send test Ether to addresses on the {Networks.kovan} testnet (networkId{' '} - {constants.NETWORK_ID_KOVAN}). Please make sure you are connected to the {Networks.kovan}{' '} + Our faucet can only send test Ether to addresses on the {Networks.Kovan} testnet (networkId{' '} + {constants.NETWORK_ID_KOVAN}). Please make sure you are connected to the {Networks.Kovan}{' '} testnet and try requesting ether again.
    ); diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx index a5ecd9e1c..7ad1d3b9c 100644 --- a/packages/website/ts/pages/documentation/documentation.tsx +++ b/packages/website/ts/pages/documentation/documentation.tsx @@ -40,9 +40,9 @@ import { utils } from 'ts/utils/utils'; const SCROLL_TOP_ID = 'docsScrollTop'; const networkNameToColor: { [network: string]: string } = { - [Networks.kovan]: colors.purple, - [Networks.ropsten]: colors.red, - [Networks.mainnet]: colors.turquois, + [Networks.Kovan]: colors.purple, + [Networks.Ropsten]: colors.red, + [Networks.Mainnet]: colors.turquois, }; export interface DocumentationAllProps { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 29eebcfe8..c48c88cae 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -592,10 +592,10 @@ export interface AddressByContractName { } export enum Networks { - mainnet = 'Mainnet', - kovan = 'Kovan', - ropsten = 'Ropsten', - rinkeby = 'Rinkeby', + Mainnet = 'Mainnet', + Kovan = 'Kovan', + Ropsten = 'Ropsten', + Rinkeby = 'Rinkeby', } export enum AbiTypes { diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index 34360cf43..874ad04c2 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -21,19 +21,19 @@ export const configs = { BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', CONTRACT_ADDRESS: { '1.0.0': { - [Networks.mainnet]: { + [Networks.Mainnet]: { [SmartContractDocSections.Exchange]: '0x12459c951127e0c374ff9105dda097662a027093', [SmartContractDocSections.TokenTransferProxy]: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4', [SmartContractDocSections.ZRXToken]: '0xe41d2489571d322189246dafa5ebde1f4699f498', [SmartContractDocSections.TokenRegistry]: '0x926a74c5c36adf004c87399e65f75628b0f98d2c', }, - [Networks.ropsten]: { + [Networks.Ropsten]: { [SmartContractDocSections.Exchange]: '0x479cc461fecd078f766ecc58533d6f69580cf3ac', [SmartContractDocSections.TokenTransferProxy]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6', [SmartContractDocSections.ZRXToken]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d', [SmartContractDocSections.TokenRegistry]: '0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed', }, - [Networks.kovan]: { + [Networks.Kovan]: { [SmartContractDocSections.Exchange]: '0x90fe2af704b34e0224bf2299c838e04d4dcf1364', [SmartContractDocSections.TokenTransferProxy]: '0x087Eed4Bc1ee3DE49BeFbd66C662B434B15d49d4', [SmartContractDocSections.ZRXToken]: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 4d6cb06c9..26a793f38 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -24,16 +24,16 @@ export const constants = { NETWORK_ID_KOVAN: 42, NETWORK_ID_TESTRPC: 50, NETWORK_NAME_BY_ID: { - 1: Networks.mainnet, - 3: Networks.ropsten, - 4: Networks.rinkeby, - 42: Networks.kovan, + 1: Networks.Mainnet, + 3: Networks.Ropsten, + 4: Networks.Rinkeby, + 42: Networks.Kovan, } as { [symbol: number]: string }, NETWORK_ID_BY_NAME: { - [Networks.mainnet]: 1, - [Networks.ropsten]: 3, - [Networks.rinkeby]: 4, - [Networks.kovan]: 42, + [Networks.Mainnet]: 1, + [Networks.Ropsten]: 3, + [Networks.Rinkeby]: 4, + [Networks.Kovan]: 42, } as { [networkName: string]: number }, NULL_ADDRESS: '0x0000000000000000000000000000000000000000', PROVIDER_NAME_LEDGER: 'Ledger', diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 003ecd30a..7e69b1c5f 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -151,7 +151,7 @@ export const utils = { if (_.isUndefined(networkName)) { return undefined; } - const etherScanPrefix = networkName === Networks.mainnet ? '' : `${networkName.toLowerCase()}.`; + const etherScanPrefix = networkName === Networks.Mainnet ? '' : `${networkName.toLowerCase()}.`; return `https://${etherScanPrefix}etherscan.io/${suffix}/${addressOrTxHash}`; }, setUrlHash(anchorId: string) { -- cgit From d3e42e4b3e2f893b616246d35f46dba92f251e83 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 21:16:47 +0100 Subject: Fix prettier --- packages/website/ts/components/portal.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index 1c4937654..92589f75c 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -23,15 +23,7 @@ import { localStorage } from 'ts/local_storage/local_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; import { orderSchema } from 'ts/schemas/order_schema'; import { SchemaValidator } from 'ts/schemas/validator'; -import { - BlockchainErrs, - HashData, - Order, - ProviderType, - ScreenWidths, - TokenByAddress, - WebsitePaths, -} from 'ts/types'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress, WebsitePaths } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; -- cgit From 8aac6e46d4f23fe8eee6116aeb9570945dc9df23 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 21:34:13 +0100 Subject: Remove unused prop --- packages/website/ts/components/eth_wrappers.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index db1ceecb5..c2cdf6751 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -309,7 +309,6 @@ export class EthWrappers extends React.Component Date: Tue, 30 Jan 2018 14:02:13 -0800 Subject: Add protected keyword to underscore lint rule --- packages/tslint-config/CHANGELOG.md | 5 ++ .../rules/underscorePrivateAndProtectedRule.ts | 61 ++++++++++++++++++++++ .../tslint-config/rules/underscorePrivatesRule.ts | 61 ---------------------- packages/tslint-config/tslint.json | 2 +- 4 files changed, 67 insertions(+), 62 deletions(-) create mode 100644 packages/tslint-config/rules/underscorePrivateAndProtectedRule.ts delete mode 100644 packages/tslint-config/rules/underscorePrivatesRule.ts diff --git a/packages/tslint-config/CHANGELOG.md b/packages/tslint-config/CHANGELOG.md index 1d56bca5b..dac4051ea 100644 --- a/packages/tslint-config/CHANGELOG.md +++ b/packages/tslint-config/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## v0.5.0 - _TBD, 2018_ + + * Modified custom 'underscore-privates' rule, changing it to 'underscore-private-protected' requiring underscores to be prepended to private variable names + * Because our tools can be used in both a TS and JS environment, we want to make the private methods of any public facing interface show up at the bottom of auto-complete lists. Additionally, we wanted to remain consistent with respect to our usage of underscores in order to enforce this rule with a linter rule, rather then manual code reviews. + ## v0.4.0 - _December 28, 2017_ * Added custom 'underscore-privates' rule, requiring underscores to be prepended to private variable names diff --git a/packages/tslint-config/rules/underscorePrivateAndProtectedRule.ts b/packages/tslint-config/rules/underscorePrivateAndProtectedRule.ts new file mode 100644 index 000000000..2f05b0c18 --- /dev/null +++ b/packages/tslint-config/rules/underscorePrivateAndProtectedRule.ts @@ -0,0 +1,61 @@ +import * as Lint from 'tslint'; +import * as ts from 'typescript'; + +const UNDERSCORE = '_'; + +type RelevantClassMember = + | ts.MethodDeclaration + | ts.PropertyDeclaration + | ts.GetAccessorDeclaration + | ts.SetAccessorDeclaration; + +// Copied from: https://github.com/DanielRosenwasser/underscore-privates-tslint-rule +// The version on github is not published on npm +export class Rule extends Lint.Rules.AbstractRule { + public static FAILURE_STRING = 'private and protected members must be prefixed with an underscore'; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} +function walk(ctx: Lint.WalkContext): void { + traverse(ctx.sourceFile); + + function traverse(node: ts.Node): void { + checkNodeForViolations(ctx, node); + return ts.forEachChild(node, traverse); + } +} +function checkNodeForViolations(ctx: Lint.WalkContext, node: ts.Node): void { + if (!isRelevantClassMember(node)) { + return; + } + // The declaration might have a computed property name or a numeric name. + const name = node.name; + if (!nameIsIdentifier(name)) { + return; + } + if (!nameStartsWithUnderscore(name.text) && memberIsPrivate(node)) { + ctx.addFailureAtNode(name, Rule.FAILURE_STRING); + } +} +function isRelevantClassMember(node: ts.Node): node is RelevantClassMember { + switch (node.kind) { + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + return true; + default: + return false; + } +} +function nameStartsWithUnderscore(text: string) { + return text.charCodeAt(0) === UNDERSCORE.charCodeAt(0); +} +function memberIsPrivate(node: ts.Declaration) { + return Lint.hasModifier(node.modifiers, ts.SyntaxKind.PrivateKeyword, ts.SyntaxKind.ProtectedKeyword); +} +function nameIsIdentifier(node: ts.Node): node is ts.Identifier { + return node.kind === ts.SyntaxKind.Identifier; +} diff --git a/packages/tslint-config/rules/underscorePrivatesRule.ts b/packages/tslint-config/rules/underscorePrivatesRule.ts deleted file mode 100644 index 472ea09ff..000000000 --- a/packages/tslint-config/rules/underscorePrivatesRule.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as Lint from 'tslint'; -import * as ts from 'typescript'; - -const UNDERSCORE = '_'; - -type RelevantClassMember = - | ts.MethodDeclaration - | ts.PropertyDeclaration - | ts.GetAccessorDeclaration - | ts.SetAccessorDeclaration; - -// Copied from: https://github.com/DanielRosenwasser/underscore-privates-tslint-rule -// The version on github is not published on npm -export class Rule extends Lint.Rules.AbstractRule { - public static FAILURE_STRING = 'private members must be prefixed with an underscore'; - - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithFunction(sourceFile, walk); - } -} -function walk(ctx: Lint.WalkContext): void { - traverse(ctx.sourceFile); - - function traverse(node: ts.Node): void { - checkNodeForViolations(ctx, node); - return ts.forEachChild(node, traverse); - } -} -function checkNodeForViolations(ctx: Lint.WalkContext, node: ts.Node): void { - if (!isRelevantClassMember(node)) { - return; - } - // The declaration might have a computed property name or a numeric name. - const name = node.name; - if (!nameIsIdentifier(name)) { - return; - } - if (!nameStartsWithUnderscore(name.text) && memberIsPrivate(node)) { - ctx.addFailureAtNode(name, Rule.FAILURE_STRING); - } -} -function isRelevantClassMember(node: ts.Node): node is RelevantClassMember { - switch (node.kind) { - case ts.SyntaxKind.MethodDeclaration: - case ts.SyntaxKind.PropertyDeclaration: - case ts.SyntaxKind.GetAccessor: - case ts.SyntaxKind.SetAccessor: - return true; - default: - return false; - } -} -function nameStartsWithUnderscore(text: string) { - return text.charCodeAt(0) === UNDERSCORE.charCodeAt(0); -} -function memberIsPrivate(node: ts.Declaration) { - return Lint.hasModifier(node.modifiers, ts.SyntaxKind.PrivateKeyword); -} -function nameIsIdentifier(node: ts.Node): node is ts.Identifier { - return node.kind === ts.SyntaxKind.Identifier; -} diff --git a/packages/tslint-config/tslint.json b/packages/tslint-config/tslint.json index 44a8517dc..3266b022f 100644 --- a/packages/tslint-config/tslint.json +++ b/packages/tslint-config/tslint.json @@ -73,7 +73,7 @@ ], "space-within-parens": false, "type-literal-delimiter": true, - "underscore-privates": true, + "underscore-private-and-protected": true, "variable-name": [true, "ban-keywords", "allow-pascal-case"], "whitespace": [ true, -- cgit From fa35768fc9647c475f8132c6b431102a30b4efe1 Mon Sep 17 00:00:00 2001 From: Olaf Tomalka Date: Wed, 31 Jan 2018 00:41:58 +0100 Subject: Removed deprecated CLI options --- packages/abi-gen/src/index.ts | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index 3628adff8..5412d11ce 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -17,11 +17,9 @@ import { utils } from './utils'; const ABI_TYPE_CONSTRUCTOR = 'constructor'; const ABI_TYPE_METHOD = 'function'; const ABI_TYPE_EVENT = 'event'; -const MAIN_TEMPLATE_NAME = 'contract.mustache'; const args = yargs .option('abis', { - alias: ['abiGlob'], describe: 'Glob pattern to search for ABI JSON files', type: 'string', demandOption: true, @@ -41,20 +39,9 @@ const args = yargs .option('template', { describe: 'Path for the main template file that will be used to generate each contract', type: 'string', + demandOption: true, normalize: true, }) - .group(['templates'], 'Deprecated') - .option('templates', { - describe: 'Folder where to search for templates', - type: 'string', - }) - .conflicts('templates', ['partials', 'template-file']) - .check(argv => { - if (!argv.template && !argv.templates) { - throw new Error('You need specify the location of the template'); - } - return true; - }) .example("$0 --abis 'src/artifacts/**/*.json' --out 'src/contracts/generated/' --partials 'src/templates/partials/**/*.handlebars' --template 'src/templates/contract.handlebars'", 'Full usage example') .argv; @@ -81,11 +68,7 @@ Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamK if (args.partials) { registerPartials(args.partials); } -if (args.templates) { - registerPartials(`${args.templates}/partials/**/*.{mustache,handlebars}`); -} - -const mainTemplate = utils.getNamedContent(args.template ? args.template : `${args.templates}/${MAIN_TEMPLATE_NAME}`); +const mainTemplate = utils.getNamedContent(args.template); const template = Handlebars.compile(mainTemplate.content); const abiFileNames = globSync(args.abis); -- cgit From 7cc4a8f5cebb41ee5b9a37a1732d7f2af5d16d4a Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 30 Jan 2018 16:26:42 -0800 Subject: Fix lint errors --- .../0x.js/contract_templates/contract.mustache | 2 +- .../contract_templates/partials/call.mustache | 4 +-- .../0x.js/contract_templates/partials/tx.mustache | 10 +++--- .../src/contract_wrappers/contract_wrapper.ts | 2 +- .../src/contract_wrappers/ether_token_wrapper.ts | 6 ++-- .../src/contract_wrappers/exchange_wrapper.ts | 6 ++-- .../contract_wrappers/generated/base_contract.ts | 12 ++++---- .../0x.js/src/contract_wrappers/token_wrapper.ts | 6 ++-- packages/0x.js/test/ether_token_wrapper_test.ts | 2 +- packages/0x.js/test/exchange_wrapper_test.ts | 2 +- packages/0x.js/test/subscription_test.ts | 4 +-- packages/0x.js/test/token_wrapper_test.ts | 2 +- .../testnet-faucets/src/ts/ether_request_queue.ts | 6 ++-- packages/testnet-faucets/src/ts/request_queue.ts | 36 +++++++++++----------- .../testnet-faucets/src/ts/zrx_request_queue.ts | 4 +-- packages/tslint-config/CHANGELOG.md | 3 +- packages/website/ts/blockchain.ts | 2 +- 17 files changed, 54 insertions(+), 55 deletions(-) diff --git a/packages/0x.js/contract_templates/contract.mustache b/packages/0x.js/contract_templates/contract.mustache index 3e501cce6..d3fe1b8cc 100644 --- a/packages/0x.js/contract_templates/contract.mustache +++ b/packages/0x.js/contract_templates/contract.mustache @@ -20,6 +20,6 @@ export class {{contractName}}Contract extends BaseContract { {{/each}} constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial) { super(web3ContractInstance, defaults); - classUtils.bindAll(this, ['web3ContractInstance', 'defaults']); + classUtils.bindAll(this, ['_web3ContractInstance', '_defaults']); } } // tslint:disable:max-file-line-count diff --git a/packages/0x.js/contract_templates/partials/call.mustache b/packages/0x.js/contract_templates/partials/call.mustache index ef4bda724..0475136f0 100644 --- a/packages/0x.js/contract_templates/partials/call.mustache +++ b/packages/0x.js/contract_templates/partials/call.mustache @@ -5,8 +5,8 @@ public {{this.name}} = { ): Promise<{{> return_type outputs=outputs}}> { const self = this as {{contractName}}Contract; const result = await promisify<{{> return_type outputs=outputs}}>( - self.web3ContractInstance.{{this.name}}.call, - self.web3ContractInstance, + self._web3ContractInstance.{{this.name}}.call, + self._web3ContractInstance, )( {{> params inputs=inputs}} ); diff --git a/packages/0x.js/contract_templates/partials/tx.mustache b/packages/0x.js/contract_templates/partials/tx.mustache index 8a43e5319..9df83266a 100644 --- a/packages/0x.js/contract_templates/partials/tx.mustache +++ b/packages/0x.js/contract_templates/partials/tx.mustache @@ -9,7 +9,7 @@ public {{this.name}} = { {{/this.payable}} ): Promise { const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( + const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( txData, self.{{this.name}}.estimateGasAsync.bind( self, @@ -17,7 +17,7 @@ public {{this.name}} = { ), ); const txHash = await promisify( - self.web3ContractInstance.{{this.name}}, self.web3ContractInstance, + self._web3ContractInstance.{{this.name}}, self._web3ContractInstance, )( {{> params inputs=inputs}} txDataWithDefaults, @@ -29,11 +29,11 @@ public {{this.name}} = { txData: TxData = {}, ): Promise { const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( + const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( txData, ); const gas = await promisify( - self.web3ContractInstance.{{this.name}}.estimateGas, self.web3ContractInstance, + self._web3ContractInstance.{{this.name}}.estimateGas, self._web3ContractInstance, )( {{> params inputs=inputs}} txDataWithDefaults, @@ -45,7 +45,7 @@ public {{this.name}} = { txData: TxData = {}, ): string { const self = this as {{contractName}}Contract; - const abiEncodedTransactionData = self.web3ContractInstance.{{this.name}}.getData(); + const abiEncodedTransactionData = self._web3ContractInstance.{{this.name}}.getData(); return abiEncodedTransactionData; }, }; diff --git a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts index 5f11d810a..873489dc9 100644 --- a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts @@ -52,7 +52,7 @@ export class ContractWrapper { this._onLogAddedSubscriptionToken = undefined; this._onLogRemovedSubscriptionToken = undefined; } - protected unsubscribeAll(): void { + protected _unsubscribeAll(): void { const filterTokens = _.keys(this._filterCallbacks); _.each(filterTokens, filterToken => { this._unsubscribe(filterToken); diff --git a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts index 8fa7aa78b..cbafcfe94 100644 --- a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts @@ -159,11 +159,11 @@ export class EtherTokenWrapper extends ContractWrapper { /** * Cancels all existing subscriptions */ - public unsubscribeAll(): void { - super.unsubscribeAll(); + public _unsubscribeAll(): void { + super._unsubscribeAll(); } private _invalidateContractInstance(): void { - this.unsubscribeAll(); + this._unsubscribeAll(); this._etherTokenContractsByAddress = {}; } private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise { diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts index fceab851a..63c0d073a 100644 --- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts @@ -678,8 +678,8 @@ export class ExchangeWrapper extends ContractWrapper { /** * Cancels all existing subscriptions */ - public unsubscribeAll(): void { - super.unsubscribeAll(); + public _unsubscribeAll(): void { + super._unsubscribeAll(); } /** * Gets historical logs without creating a subscription @@ -861,7 +861,7 @@ export class ExchangeWrapper extends ContractWrapper { return contractAddress; } private _invalidateContractInstances(): void { - this.unsubscribeAll(); + this._unsubscribeAll(); delete this._exchangeContractIfExists; } private async _isValidSignatureUsingContractCallAsync( diff --git a/packages/0x.js/src/contract_wrappers/generated/base_contract.ts b/packages/0x.js/src/contract_wrappers/generated/base_contract.ts index 28a7e2f52..d8fac7eea 100644 --- a/packages/0x.js/src/contract_wrappers/generated/base_contract.ts +++ b/packages/0x.js/src/contract_wrappers/generated/base_contract.ts @@ -3,9 +3,9 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; export class BaseContract { - protected web3ContractInstance: Web3.ContractInstance; - protected defaults: Partial; - protected async applyDefaultsToTxDataAsync( + protected _web3ContractInstance: Web3.ContractInstance; + protected _defaults: Partial; + protected async _applyDefaultsToTxDataAsync( txData: T, estimateGasAsync?: (txData: T) => Promise, ): Promise { @@ -15,7 +15,7 @@ export class BaseContract { // 3. Gas estimate calculation + safety margin const removeUndefinedProperties = _.pickBy; const txDataWithDefaults = { - ...removeUndefinedProperties(this.defaults), + ...removeUndefinedProperties(this._defaults), ...removeUndefinedProperties(txData as any), // HACK: TS can't prove that T is spreadable. // Awaiting https://github.com/Microsoft/TypeScript/pull/13288 to be merged @@ -27,7 +27,7 @@ export class BaseContract { return txDataWithDefaults; } constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial) { - this.web3ContractInstance = web3ContractInstance; - this.defaults = defaults; + this._web3ContractInstance = web3ContractInstance; + this._defaults = defaults; } } diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts index 4216ff462..98c24d059 100644 --- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts @@ -342,8 +342,8 @@ export class TokenWrapper extends ContractWrapper { /** * Cancels all existing subscriptions */ - public unsubscribeAll(): void { - super.unsubscribeAll(); + public _unsubscribeAll(): void { + super._unsubscribeAll(); } /** * Gets historical logs without creating a subscription @@ -374,7 +374,7 @@ export class TokenWrapper extends ContractWrapper { return logs; } private _invalidateContractInstances(): void { - this.unsubscribeAll(); + this._unsubscribeAll(); this._tokenContractsByAddress = {}; } private async _getTokenContractAsync(tokenAddress: string): Promise { diff --git a/packages/0x.js/test/ether_token_wrapper_test.ts b/packages/0x.js/test/ether_token_wrapper_test.ts index 6069b42bf..9716abab8 100644 --- a/packages/0x.js/test/ether_token_wrapper_test.ts +++ b/packages/0x.js/test/ether_token_wrapper_test.ts @@ -144,7 +144,7 @@ describe('EtherTokenWrapper', () => { etherTokenAddress = etherToken.address; }); afterEach(() => { - zeroEx.etherToken.unsubscribeAll(); + zeroEx.etherToken._unsubscribeAll(); }); // Hack: Mocha does not allow a test to be both async and have a `done` callback // Since we need to await the receipt of the event in the `subscribe` callback, diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts index c15cd65a9..044298601 100644 --- a/packages/0x.js/test/exchange_wrapper_test.ts +++ b/packages/0x.js/test/exchange_wrapper_test.ts @@ -921,7 +921,7 @@ describe('ExchangeWrapper', () => { ); }); afterEach(async () => { - zeroEx.exchange.unsubscribeAll(); + zeroEx.exchange._unsubscribeAll(); }); // Hack: Mocha does not allow a test to be both async and have a `done` callback // Since we need to await the receipt of the event in the `subscribe` callback, diff --git a/packages/0x.js/test/subscription_test.ts b/packages/0x.js/test/subscription_test.ts index f485bf84b..337e2effa 100644 --- a/packages/0x.js/test/subscription_test.ts +++ b/packages/0x.js/test/subscription_test.ts @@ -49,7 +49,7 @@ describe('SubscriptionTest', () => { tokenAddress = token.address; }); afterEach(() => { - zeroEx.token.unsubscribeAll(); + zeroEx.token._unsubscribeAll(); _.each(stubs, s => s.restore()); stubs = []; }); @@ -76,7 +76,7 @@ describe('SubscriptionTest', () => { const callback = (err: Error | null, logEvent?: DecodedLogEvent) => _.noop; zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error('JSON RPC error'))]; - zeroEx.token.unsubscribeAll(); + zeroEx.token._unsubscribeAll(); done(); })().catch(done); }); diff --git a/packages/0x.js/test/token_wrapper_test.ts b/packages/0x.js/test/token_wrapper_test.ts index 070d6ec47..34ebe30c2 100644 --- a/packages/0x.js/test/token_wrapper_test.ts +++ b/packages/0x.js/test/token_wrapper_test.ts @@ -377,7 +377,7 @@ describe('TokenWrapper', () => { tokenAddress = token.address; }); afterEach(() => { - zeroEx.token.unsubscribeAll(); + zeroEx.token._unsubscribeAll(); }); // Hack: Mocha does not allow a test to be both async and have a `done` callback // Since we need to await the receipt of the event in the `subscribe` callback, diff --git a/packages/testnet-faucets/src/ts/ether_request_queue.ts b/packages/testnet-faucets/src/ts/ether_request_queue.ts index 1c4b19ab9..710d49f39 100644 --- a/packages/testnet-faucets/src/ts/ether_request_queue.ts +++ b/packages/testnet-faucets/src/ts/ether_request_queue.ts @@ -9,14 +9,14 @@ import { utils } from './utils'; const DISPENSE_AMOUNT_ETHER = 0.1; export class EtherRequestQueue extends RequestQueue { - protected async processNextRequestFireAndForgetAsync(recipientAddress: string) { + protected async _processNextRequestFireAndForgetAsync(recipientAddress: string) { utils.consoleLog(`Processing ETH ${recipientAddress}`); - const sendTransactionAsync = promisify(this.web3.eth.sendTransaction); + const sendTransactionAsync = promisify(this._web3.eth.sendTransaction); try { const txHash = await sendTransactionAsync({ from: configs.DISPENSER_ADDRESS, to: recipientAddress, - value: this.web3.toWei(DISPENSE_AMOUNT_ETHER, 'ether'), + value: this._web3.toWei(DISPENSE_AMOUNT_ETHER, 'ether'), }); utils.consoleLog(`Sent ${DISPENSE_AMOUNT_ETHER} ETH to ${recipientAddress} tx: ${txHash}`); } catch (err) { diff --git a/packages/testnet-faucets/src/ts/request_queue.ts b/packages/testnet-faucets/src/ts/request_queue.ts index 20f2833a1..7d685522a 100644 --- a/packages/testnet-faucets/src/ts/request_queue.ts +++ b/packages/testnet-faucets/src/ts/request_queue.ts @@ -11,46 +11,46 @@ const MAX_QUEUE_SIZE = 500; const DEFAULT_QUEUE_INTERVAL_MS = 1000; export class RequestQueue { - protected queueIntervalMs: number; - protected queue: string[]; - protected queueIntervalId: NodeJS.Timer; - protected web3: Web3; + protected _queueIntervalMs: number; + protected _queue: string[]; + protected _queueIntervalId: NodeJS.Timer; + protected _web3: Web3; constructor(web3: any) { - this.queueIntervalMs = DEFAULT_QUEUE_INTERVAL_MS; - this.queue = []; + this._queueIntervalMs = DEFAULT_QUEUE_INTERVAL_MS; + this._queue = []; - this.web3 = web3; + this._web3 = web3; - this.start(); + this._start(); } public add(recipientAddress: string): boolean { if (this.isFull()) { return false; } - this.queue.push(recipientAddress); + this._queue.push(recipientAddress); return true; } public size(): number { - return this.queue.length; + return this._queue.length; } public isFull(): boolean { return this.size() >= MAX_QUEUE_SIZE; } - protected start() { - this.queueIntervalId = timers.setInterval(() => { - const recipientAddress = this.queue.shift(); + protected _start() { + this._queueIntervalId = timers.setInterval(() => { + const recipientAddress = this._queue.shift(); if (_.isUndefined(recipientAddress)) { return; } // tslint:disable-next-line:no-floating-promises - this.processNextRequestFireAndForgetAsync(recipientAddress); - }, this.queueIntervalMs); + this._processNextRequestFireAndForgetAsync(recipientAddress); + }, this._queueIntervalMs); } - protected stop() { - clearInterval(this.queueIntervalId); + protected _stop() { + clearInterval(this._queueIntervalId); } // tslint:disable-next-line:prefer-function-over-method - protected async processNextRequestFireAndForgetAsync(recipientAddress: string) { + protected async _processNextRequestFireAndForgetAsync(recipientAddress: string) { throw new Error('Expected processNextRequestFireAndForgetAsync to be implemented by a subclass'); } } diff --git a/packages/testnet-faucets/src/ts/zrx_request_queue.ts b/packages/testnet-faucets/src/ts/zrx_request_queue.ts index 3d73f9dd2..db1b619a8 100644 --- a/packages/testnet-faucets/src/ts/zrx_request_queue.ts +++ b/packages/testnet-faucets/src/ts/zrx_request_queue.ts @@ -20,13 +20,13 @@ export class ZRXRequestQueue extends RequestQueue { private _zeroEx: ZeroEx; constructor(web3: Web3, networkId: number) { super(web3); - this.queueIntervalMs = QUEUE_INTERVAL_MS; + this._queueIntervalMs = QUEUE_INTERVAL_MS; const zeroExConfig = { networkId, }; this._zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig); } - protected async processNextRequestFireAndForgetAsync(recipientAddress: string) { + protected async _processNextRequestFireAndForgetAsync(recipientAddress: string) { utils.consoleLog(`Processing ZRX ${recipientAddress}`); const baseUnitAmount = ZeroEx.toBaseUnitAmount(DISPENSE_AMOUNT_ZRX, 18); try { diff --git a/packages/tslint-config/CHANGELOG.md b/packages/tslint-config/CHANGELOG.md index dac4051ea..f7dc6e5a4 100644 --- a/packages/tslint-config/CHANGELOG.md +++ b/packages/tslint-config/CHANGELOG.md @@ -2,8 +2,7 @@ ## v0.5.0 - _TBD, 2018_ - * Modified custom 'underscore-privates' rule, changing it to 'underscore-private-protected' requiring underscores to be prepended to private variable names - * Because our tools can be used in both a TS and JS environment, we want to make the private methods of any public facing interface show up at the bottom of auto-complete lists. Additionally, we wanted to remain consistent with respect to our usage of underscores in order to enforce this rule with a linter rule, rather then manual code reviews. + * Modified custom 'underscore-privates' rule, changing it to 'underscore-private-and-protected' requiring underscores to be prepended to both private and protected variable names ## v0.4.0 - _December 28, 2017_ diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index d53994c0c..71995e2cd 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -689,7 +689,7 @@ export class Blockchain { } } private _stopWatchingExchangeLogFillEvents(): void { - this._zeroEx.exchange.unsubscribeAll(); + this._zeroEx.exchange._unsubscribeAll(); } private async _getTokenRegistryTokensByAddressAsync(): Promise { utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); -- cgit From ca55cc99edee3697646e2b4f28622f345c290fd7 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 30 Jan 2018 16:48:40 -0800 Subject: Add PR number to changelog --- packages/tslint-config/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tslint-config/CHANGELOG.md b/packages/tslint-config/CHANGELOG.md index f7dc6e5a4..04d705941 100644 --- a/packages/tslint-config/CHANGELOG.md +++ b/packages/tslint-config/CHANGELOG.md @@ -2,7 +2,7 @@ ## v0.5.0 - _TBD, 2018_ - * Modified custom 'underscore-privates' rule, changing it to 'underscore-private-and-protected' requiring underscores to be prepended to both private and protected variable names + * Modified custom 'underscore-privates' rule, changing it to 'underscore-private-and-protected' requiring underscores to be prepended to both private and protected variable names (#354) ## v0.4.0 - _December 28, 2017_ -- cgit From eaeb715e56bbb3243268ee5fd863cfd1f1211c39 Mon Sep 17 00:00:00 2001 From: Olaf Tomalka Date: Wed, 31 Jan 2018 14:24:20 +0100 Subject: Updated contract generation in 0x to new abi-gen CLI --- .../0x.js/contract_templates/contract.handlebars | 25 +++++++++++ .../0x.js/contract_templates/contract.mustache | 25 ----------- .../contract_templates/partials/call.handlebars | 15 +++++++ .../contract_templates/partials/call.mustache | 15 ------- .../contract_templates/partials/params.handlebars | 3 ++ .../contract_templates/partials/params.mustache | 3 -- .../partials/return_type.handlebars | 6 +++ .../partials/return_type.mustache | 6 --- .../contract_templates/partials/tx.handlebars | 51 ++++++++++++++++++++++ .../0x.js/contract_templates/partials/tx.mustache | 51 ---------------------- .../partials/typed_params.handlebars | 3 ++ .../partials/typed_params.mustache | 3 -- packages/0x.js/package.json | 2 +- 13 files changed, 104 insertions(+), 104 deletions(-) create mode 100644 packages/0x.js/contract_templates/contract.handlebars delete mode 100644 packages/0x.js/contract_templates/contract.mustache create mode 100644 packages/0x.js/contract_templates/partials/call.handlebars delete mode 100644 packages/0x.js/contract_templates/partials/call.mustache create mode 100644 packages/0x.js/contract_templates/partials/params.handlebars delete mode 100644 packages/0x.js/contract_templates/partials/params.mustache create mode 100644 packages/0x.js/contract_templates/partials/return_type.handlebars delete mode 100644 packages/0x.js/contract_templates/partials/return_type.mustache create mode 100644 packages/0x.js/contract_templates/partials/tx.handlebars delete mode 100644 packages/0x.js/contract_templates/partials/tx.mustache create mode 100644 packages/0x.js/contract_templates/partials/typed_params.handlebars delete mode 100644 packages/0x.js/contract_templates/partials/typed_params.mustache diff --git a/packages/0x.js/contract_templates/contract.handlebars b/packages/0x.js/contract_templates/contract.handlebars new file mode 100644 index 000000000..3e501cce6 --- /dev/null +++ b/packages/0x.js/contract_templates/contract.handlebars @@ -0,0 +1,25 @@ +/** + * This file is auto-generated using abi-gen. Don't edit directly. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. + */ +// tslint:disable-next-line:no-unused-variable +import { TxData, TxDataPayable } from '@0xproject/types'; +import { BigNumber, classUtils, promisify } from '@0xproject/utils'; +import * as Web3 from 'web3'; + +import {BaseContract} from './base_contract'; + +export class {{contractName}}Contract extends BaseContract { +{{#each methods}} + {{#this.constant}} + {{> call contractName=../contractName}} + {{/this.constant}} + {{^this.constant}} + {{> tx contractName=../contractName}} + {{/this.constant}} +{{/each}} + constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial) { + super(web3ContractInstance, defaults); + classUtils.bindAll(this, ['web3ContractInstance', 'defaults']); + } +} // tslint:disable:max-file-line-count diff --git a/packages/0x.js/contract_templates/contract.mustache b/packages/0x.js/contract_templates/contract.mustache deleted file mode 100644 index 3e501cce6..000000000 --- a/packages/0x.js/contract_templates/contract.mustache +++ /dev/null @@ -1,25 +0,0 @@ -/** - * This file is auto-generated using abi-gen. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. - */ -// tslint:disable-next-line:no-unused-variable -import { TxData, TxDataPayable } from '@0xproject/types'; -import { BigNumber, classUtils, promisify } from '@0xproject/utils'; -import * as Web3 from 'web3'; - -import {BaseContract} from './base_contract'; - -export class {{contractName}}Contract extends BaseContract { -{{#each methods}} - {{#this.constant}} - {{> call contractName=../contractName}} - {{/this.constant}} - {{^this.constant}} - {{> tx contractName=../contractName}} - {{/this.constant}} -{{/each}} - constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial) { - super(web3ContractInstance, defaults); - classUtils.bindAll(this, ['web3ContractInstance', 'defaults']); - } -} // tslint:disable:max-file-line-count diff --git a/packages/0x.js/contract_templates/partials/call.handlebars b/packages/0x.js/contract_templates/partials/call.handlebars new file mode 100644 index 000000000..ef4bda724 --- /dev/null +++ b/packages/0x.js/contract_templates/partials/call.handlebars @@ -0,0 +1,15 @@ +public {{this.name}} = { + async callAsync( + {{> typed_params inputs=inputs}} + defaultBlock?: Web3.BlockParam, + ): Promise<{{> return_type outputs=outputs}}> { + const self = this as {{contractName}}Contract; + const result = await promisify<{{> return_type outputs=outputs}}>( + self.web3ContractInstance.{{this.name}}.call, + self.web3ContractInstance, + )( + {{> params inputs=inputs}} + ); + return result; + }, +}; diff --git a/packages/0x.js/contract_templates/partials/call.mustache b/packages/0x.js/contract_templates/partials/call.mustache deleted file mode 100644 index ef4bda724..000000000 --- a/packages/0x.js/contract_templates/partials/call.mustache +++ /dev/null @@ -1,15 +0,0 @@ -public {{this.name}} = { - async callAsync( - {{> typed_params inputs=inputs}} - defaultBlock?: Web3.BlockParam, - ): Promise<{{> return_type outputs=outputs}}> { - const self = this as {{contractName}}Contract; - const result = await promisify<{{> return_type outputs=outputs}}>( - self.web3ContractInstance.{{this.name}}.call, - self.web3ContractInstance, - )( - {{> params inputs=inputs}} - ); - return result; - }, -}; diff --git a/packages/0x.js/contract_templates/partials/params.handlebars b/packages/0x.js/contract_templates/partials/params.handlebars new file mode 100644 index 000000000..ac5d4ae85 --- /dev/null +++ b/packages/0x.js/contract_templates/partials/params.handlebars @@ -0,0 +1,3 @@ +{{#each inputs}} +{{name}}, +{{/each}} diff --git a/packages/0x.js/contract_templates/partials/params.mustache b/packages/0x.js/contract_templates/partials/params.mustache deleted file mode 100644 index ac5d4ae85..000000000 --- a/packages/0x.js/contract_templates/partials/params.mustache +++ /dev/null @@ -1,3 +0,0 @@ -{{#each inputs}} -{{name}}, -{{/each}} diff --git a/packages/0x.js/contract_templates/partials/return_type.handlebars b/packages/0x.js/contract_templates/partials/return_type.handlebars new file mode 100644 index 000000000..383961a40 --- /dev/null +++ b/packages/0x.js/contract_templates/partials/return_type.handlebars @@ -0,0 +1,6 @@ +{{#singleReturnValue}} +{{#returnType outputs.0.type}}{{/returnType}} +{{/singleReturnValue}} +{{^singleReturnValue}} +[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] +{{/singleReturnValue}} diff --git a/packages/0x.js/contract_templates/partials/return_type.mustache b/packages/0x.js/contract_templates/partials/return_type.mustache deleted file mode 100644 index 383961a40..000000000 --- a/packages/0x.js/contract_templates/partials/return_type.mustache +++ /dev/null @@ -1,6 +0,0 @@ -{{#singleReturnValue}} -{{#returnType outputs.0.type}}{{/returnType}} -{{/singleReturnValue}} -{{^singleReturnValue}} -[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] -{{/singleReturnValue}} diff --git a/packages/0x.js/contract_templates/partials/tx.handlebars b/packages/0x.js/contract_templates/partials/tx.handlebars new file mode 100644 index 000000000..8a43e5319 --- /dev/null +++ b/packages/0x.js/contract_templates/partials/tx.handlebars @@ -0,0 +1,51 @@ +public {{this.name}} = { + async sendTransactionAsync( + {{> typed_params inputs=inputs}} + {{#this.payable}} + txData: TxDataPayable = {}, + {{/this.payable}} + {{^this.payable}} + txData: TxData = {}, + {{/this.payable}} + ): Promise { + const self = this as {{contractName}}Contract; + const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( + txData, + self.{{this.name}}.estimateGasAsync.bind( + self, + {{> params inputs=inputs}} + ), + ); + const txHash = await promisify( + self.web3ContractInstance.{{this.name}}, self.web3ContractInstance, + )( + {{> params inputs=inputs}} + txDataWithDefaults, + ); + return txHash; + }, + async estimateGasAsync( + {{> typed_params inputs=inputs}} + txData: TxData = {}, + ): Promise { + const self = this as {{contractName}}Contract; + const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( + txData, + ); + const gas = await promisify( + self.web3ContractInstance.{{this.name}}.estimateGas, self.web3ContractInstance, + )( + {{> params inputs=inputs}} + txDataWithDefaults, + ); + return gas; + }, + getABIEncodedTransactionData( + {{> typed_params inputs=inputs}} + txData: TxData = {}, + ): string { + const self = this as {{contractName}}Contract; + const abiEncodedTransactionData = self.web3ContractInstance.{{this.name}}.getData(); + return abiEncodedTransactionData; + }, +}; diff --git a/packages/0x.js/contract_templates/partials/tx.mustache b/packages/0x.js/contract_templates/partials/tx.mustache deleted file mode 100644 index 8a43e5319..000000000 --- a/packages/0x.js/contract_templates/partials/tx.mustache +++ /dev/null @@ -1,51 +0,0 @@ -public {{this.name}} = { - async sendTransactionAsync( - {{> typed_params inputs=inputs}} - {{#this.payable}} - txData: TxDataPayable = {}, - {{/this.payable}} - {{^this.payable}} - txData: TxData = {}, - {{/this.payable}} - ): Promise { - const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( - txData, - self.{{this.name}}.estimateGasAsync.bind( - self, - {{> params inputs=inputs}} - ), - ); - const txHash = await promisify( - self.web3ContractInstance.{{this.name}}, self.web3ContractInstance, - )( - {{> params inputs=inputs}} - txDataWithDefaults, - ); - return txHash; - }, - async estimateGasAsync( - {{> typed_params inputs=inputs}} - txData: TxData = {}, - ): Promise { - const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( - txData, - ); - const gas = await promisify( - self.web3ContractInstance.{{this.name}}.estimateGas, self.web3ContractInstance, - )( - {{> params inputs=inputs}} - txDataWithDefaults, - ); - return gas; - }, - getABIEncodedTransactionData( - {{> typed_params inputs=inputs}} - txData: TxData = {}, - ): string { - const self = this as {{contractName}}Contract; - const abiEncodedTransactionData = self.web3ContractInstance.{{this.name}}.getData(); - return abiEncodedTransactionData; - }, -}; diff --git a/packages/0x.js/contract_templates/partials/typed_params.handlebars b/packages/0x.js/contract_templates/partials/typed_params.handlebars new file mode 100644 index 000000000..3ea4b2e95 --- /dev/null +++ b/packages/0x.js/contract_templates/partials/typed_params.handlebars @@ -0,0 +1,3 @@ +{{#each inputs}} + {{name}}: {{#parameterType type}}{{/parameterType}}, +{{/each}} diff --git a/packages/0x.js/contract_templates/partials/typed_params.mustache b/packages/0x.js/contract_templates/partials/typed_params.mustache deleted file mode 100644 index 3ea4b2e95..000000000 --- a/packages/0x.js/contract_templates/partials/typed_params.mustache +++ /dev/null @@ -1,3 +0,0 @@ -{{#each inputs}} - {{name}}: {{#parameterType type}}{{/parameterType}}, -{{/each}} diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index fc0334dd0..22ccbbc2d 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -12,7 +12,7 @@ "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json", "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", + "node ../abi-gen/lib/index.js --abis 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --template contract_templates/contract.handlebars --partials 'contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated", "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'", "test:circleci": "run-s test:coverage report_test_coverage", "test": "run-s clean test:commonjs", -- cgit From 78fbf0f7bade62a3d1c36bddf20cbe89c86aac18 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 31 Jan 2018 14:41:37 -0800 Subject: Prettier --- packages/abi-gen/CHANGELOG.md | 1 + packages/abi-gen/src/index.ts | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index 9247b315a..ffa8a7a35 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -1,6 +1,7 @@ # CHANGELOG ## v0.2.0 - _???_ + * Added CLI options for explicit specifying location of partials and main template (#346) ## v0.1.0 - _January 11, 2018_ diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index 5412d11ce..fe2b56524 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -42,8 +42,10 @@ const args = yargs demandOption: true, normalize: true, }) - .example("$0 --abis 'src/artifacts/**/*.json' --out 'src/contracts/generated/' --partials 'src/templates/partials/**/*.handlebars' --template 'src/templates/contract.handlebars'", 'Full usage example') - .argv; + .example( + "$0 --abis 'src/artifacts/**/*.json' --out 'src/contracts/generated/' --partials 'src/templates/partials/**/*.handlebars' --template 'src/templates/contract.handlebars'", + 'Full usage example', + ).argv; function registerPartials(partialsGlob: string) { const partialTemplateFileNames = globSync(partialsGlob); -- cgit