From d0916e196c5872ee9ac82941f4a9f323f5179e74 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 12 Dec 2017 14:53:39 -0600 Subject: [PATCH 001/125] progress weth page --- .../public/images/token_icons/ether_erc20.png | Bin 69772 -> 7584 bytes .../public/images/wrapped_eth_gray.png | Bin 0 -> 7649 bytes packages/website/ts/blockchain.ts | 2 + .../website/ts/components/eth_wrappers.tsx | 214 ++++++++++++++++++ packages/website/ts/components/fill_order.tsx | 1 - packages/website/ts/components/portal.tsx | 18 ++ .../website/ts/components/portal_menu.tsx | 8 + packages/website/ts/types.ts | 10 + packages/website/ts/utils/configs.ts | 13 +- yarn.lock | 22 +- 10 files changed, 285 insertions(+), 3 deletions(-) create mode 100644 packages/website/public/images/wrapped_eth_gray.png create mode 100644 packages/website/ts/components/eth_wrappers.tsx diff --git a/packages/website/public/images/token_icons/ether_erc20.png b/packages/website/public/images/token_icons/ether_erc20.png index f4154db7b34513c7c45a6365ea0070fe2ac09e54..bc8beae8bedde2837801637230df794cd61cc743 100644 GIT binary patch literal 7584 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

ec^~fK15O)NJd7* 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%0AZZa{ux=KXhhM_uk*PEG1tZj#~H_0^&G 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 P00000NkvXXu0mjfaGF%g literal 0 HcmV?d00001 diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 172ba6b52c..5bc95238b9 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -602,6 +602,8 @@ export class Blockchain { // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens // TODO: Refactor this out and pull the iconUrl directly from the TokenRegistry const iconUrl = constants.iconUrlBySymbol[t.symbol]; + // TEMPORARY HACK: Swap out for new WETH address + const address = t.symbol === 'WETH' ? '0x739E78d6bEbbDF24105a5145fA04436589d1CBd9' : t.address; const token: Token = { iconUrl, address: t.address, diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx new file mode 100644 index 0000000000..2779298706 --- /dev/null +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -0,0 +1,214 @@ +import {ZeroEx} from '0x.js'; +import BigNumber from 'bignumber.js'; +import * as _ from 'lodash'; +import Divider from 'material-ui/Divider'; +import Paper from 'material-ui/Paper'; +import RaisedButton from 'material-ui/RaisedButton'; +import {colors} from 'material-ui/styles'; +import { + Table, + TableBody, + TableHeader, + TableHeaderColumn, + TableRow, + TableRowColumn, +} from 'material-ui/Table'; +import * as moment from 'moment'; +import * as React from 'react'; +import {Blockchain} from 'ts/blockchain'; +import {LifeCycleRaisedButton} from 'ts/components/ui/lifecycle_raised_button'; +import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; +import {Dispatcher} from 'ts/redux/dispatcher'; +import { + OutdatedWrappedEther, + TokenByAddress, + TokenStateByAddress, +} from 'ts/types'; +import {configs} from 'ts/utils/configs'; +import {constants} from 'ts/utils/constants'; +import {errorReporter} from 'ts/utils/error_reporter'; +import {utils} from 'ts/utils/utils'; + +const PRECISION = 5; +const DATE_FORMAT = 'D/M/YY'; +const ICON_DIMENSION = 40; +const ETHER_ICON_PATH = '/images/ether.png'; +const OUTDATED_WETH_ICON_PATH = '/images/wrapped_eth_gray.png'; +const ETHER_TOKEN_SYMBOL = 'WETH'; + +interface EthWrappersProps { + networkId: number; + blockchain: Blockchain; + dispatcher: Dispatcher; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + userAddress: string; + userEtherBalance: BigNumber; +} + +interface EthWrappersState {} + +export class EthWrappers extends React.Component { + constructor(props: EthWrappersProps) { + super(props); + this.state = {}; + } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + const tokens = _.values(this.props.tokenByAddress); + const wethToken = _.find(tokens, {symbol: 'WETH'}); + const wethState = this.props.tokenStateByAddress[wethToken.address]; + const wethBalance = ZeroEx.toUnitAmount(wethState.balance, 18); + return ( +

+

ETH Wrapper

+ +
+
+ Wrap ETH into an ERC20-compliant Ether token +
+
+ + + + ETH Token + Balance + + {'ETH <-> WETH'} + + + + + + +
+ +
+ Ether +
+
+
+ + {this.props.userEtherBalance.toFixed(PRECISION)} ETH + + + + +
+ + +
+ +
+ Wrapped Ether +
+
+
+ + {wethBalance.toFixed(PRECISION)} WETH + + + + +
+
+
+
+
+

Outdated WETH

+ +
+ The{' '} + + canonical WETH + contract is updated when necessary. + Unwrap outdated WETH in order to
 retrieve your ETH and move it + to the updated WETH token. +
+
+ + + + WETH Version + Balance + + {'WETH -> ETH'} + + + + + {this.renderOutdatedWeths()} + +
+
+
+ ); + } + private renderOutdatedWeths() { + const rows = _.map(configs.outdatedWrappedEthers, (outdatedWETH: OutdatedWrappedEther) => { + const timestampMsRange = outdatedWETH.timestampMsRangeByNetworkId[this.props.networkId]; + const startMoment = moment(timestampMsRange.startTimestampMs); + const endMoment = moment(timestampMsRange.endTimestampMs); + return ( + + +
+ +
+ {startMoment.format(DATE_FORMAT)}-{endMoment.format(DATE_FORMAT)} +
+
+
+ + 0 WETH + + + + +
+ ); + }); + return rows; + } + private async wrapEthAsync() { + // TODO + } + private async unwrapEthAsync() { + // TODO + } +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 388c72d8e5..77a9cfddbc 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -4,7 +4,6 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import {Card, CardHeader, CardText} from 'material-ui/Card'; import Divider from 'material-ui/Divider'; -import Paper from 'material-ui/Paper'; import RaisedButton from 'material-ui/RaisedButton'; import TextField from 'material-ui/TextField'; import * as moment from 'moment'; diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index 62a5d2eacf..57e75dab34 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -9,6 +9,7 @@ import {Route, Switch} from 'react-router-dom'; import {Blockchain} from 'ts/blockchain'; import {BlockchainErrDialog} from 'ts/components/dialogs/blockchain_err_dialog'; import {PortalDisclaimerDialog} from 'ts/components/dialogs/portal_disclaimer_dialog'; +import {EthWrappers} from 'ts/components/eth_wrappers'; import {FillOrder} from 'ts/components/fill_order'; import {Footer} from 'ts/components/footer'; import {PortalMenu} from 'ts/components/portal_menu'; @@ -205,6 +206,10 @@ export class Portal extends React.Component {
{this.props.blockchainIsLoaded ? + {
); } + private renderEthWrapper() { + return ( + + ); + } private renderTradeHistory() { return ( {this.renderMenuItemWithIcon('Trade history', 'zmdi-format-list-bulleted')} + + {this.renderMenuItemWithIcon('ETH wrapper', 'zmdi-circle-o')} + ); } diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index d225e77849..05e797cebf 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -683,4 +683,14 @@ export interface DocsInfoConfig { menuSubsectionToVersionWhenIntroduced?: {[sectionName: string]: string}; } +export interface TimestampMsRange { + startTimestampMs: number; + endTimestampMs: number; +} + +export interface OutdatedWrappedEther { + address: string; + timestampMsRangeByNetworkId: {[networkId: number]: TimestampMsRange}; +} + // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index 63fcd27b60..e84dbd38fc 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -1,5 +1,5 @@ import * as _ from 'lodash'; -import {Environments} from 'ts/types'; +import {Environments, OutdatedWrappedEther} from 'ts/types'; const BASE_URL = window.location.origin; const isDevelopment = _.includes(BASE_URL, 'https://0xproject.dev:3572') || @@ -15,4 +15,15 @@ export const configs = { defaultTrackedTokenSymbols: ['WETH', 'ZRX'], lastLocalStorageFillClearanceDate: '2017-11-22', isMainnetEnabled: true, + outdatedWrappedEthers: [ + { + address: '0x05d090b51c40b020eab3bfcb6a2dff130df22e9c', + timestampMsRangeByNetworkId: { + 42: { + startTimestampMs: 1501614680000, + endTimestampMs: 1513106129000, + }, + }, + } as OutdatedWrappedEther, + ], }; diff --git a/yarn.lock b/yarn.lock index 1eec4f122c..13b5b211ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,26 @@ jsonschema "^1.2.0" lodash.values "^4.3.0" +"0x.js@0.27.1": + version "0.27.1" + resolved "https://registry.yarnpkg.com/0x.js/-/0x.js-0.27.1.tgz#e0dff70e257efbb7f54dddb55dddf2dce0b971ab" + dependencies: + "@0xproject/assert" "^0.0.6" + "@0xproject/json-schemas" "^0.6.9" + bignumber.js "~4.1.0" + bintrees "^1.0.2" + bn.js "4.11.8" + compare-versions "^3.0.1" + es6-promisify "^5.0.0" + ethereumjs-abi "^0.6.4" + ethereumjs-blockstream "^2.0.6" + ethereumjs-util "^5.1.1" + find-versions "^2.0.0" + js-sha3 "^0.6.1" + lodash "^4.17.4" + uuid "^3.1.0" + web3 "^0.20.0" + "0x.js@^0.22.6": version "0.22.6" resolved "https://registry.yarnpkg.com/0x.js/-/0x.js-0.22.6.tgz#bc3ff79b6d71f8cf7fae3c78b2c776cfa79c193a" @@ -1345,7 +1365,7 @@ bn.js@4.11.7: version "4.11.7" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.7.tgz#ddb048e50d9482790094c13eb3fcfc833ce7ab46" -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.11.7, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: +bn.js@4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.11.7, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" From 5619780cc18a5bdc6d1ff638c413870f56cf07e0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Dec 2017 19:43:43 -0600 Subject: [PATCH 002/125] Use boxed ETH --- packages/website/public/images/ether.png | Bin 2312 -> 4235 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/website/public/images/ether.png b/packages/website/public/images/ether.png index 6a40a976dfc023b70bacd652c12486c4689b78d3..c92f456810c5f95eb9acf78259e8f9cf4512e0eb 100644 GIT binary patch literal 4235 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 iwz6Oz4c>UOa{CX;Yk5nPCxb5l0000 Date: Wed, 13 Dec 2017 19:44:27 -0600 Subject: [PATCH 003/125] Remove temporary hack --- packages/website/ts/blockchain.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 5bc95238b9..172ba6b52c 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -602,8 +602,6 @@ export class Blockchain { // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens // TODO: Refactor this out and pull the iconUrl directly from the TokenRegistry const iconUrl = constants.iconUrlBySymbol[t.symbol]; - // TEMPORARY HACK: Swap out for new WETH address - const address = t.symbol === 'WETH' ? '0x739E78d6bEbbDF24105a5145fA04436589d1CBd9' : t.address; const token: Token = { iconUrl, address: t.address, From b054080fa1da88460f52021278fb33b8d88371a3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Dec 2017 19:45:44 -0600 Subject: [PATCH 004/125] Remove ETH-WETH convert button from balances page --- .../website/ts/components/token_balances.tsx | 24 ------------------- packages/website/ts/types.ts | 1 - 2 files changed, 25 deletions(-) diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index ae5ef9222f..799fecc695 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -23,7 +23,6 @@ import * as React from 'react'; import ReactTooltip = require('react-tooltip'); import firstBy = require('thenby'); import {Blockchain} from 'ts/blockchain'; -import {EthWethConversionButton} from 'ts/components/eth_weth_conversion_button'; import {AssetPicker} from 'ts/components/generate_order/asset_picker'; import {AllowanceToggle} from 'ts/components/inputs/allowance_toggle'; import {SendButton} from 'ts/components/send_button'; @@ -425,16 +424,6 @@ export class TokenBalances extends React.Component } - {token.symbol === ETHER_TOKEN_SYMBOL && - - } {token.symbol === ZRX_TOKEN_SYMBOL && this.props.networkId === constants.TESTNET_NETWORK_ID && ); - case BalanceErrs.wethConversionFailed: - return ( -
- Converting between Ether and Ether Tokens failed unexpectedly. - Please refresh the page and try again. -
- ); - case BalanceErrs.allowanceSettingFailed: return (
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 05e797cebf..4caa090259 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -135,7 +135,6 @@ export enum BalanceErrs { faucetRequestFailed, faucetQueueIsFull, mintingFailed, - wethConversionFailed, sendFailed, allowanceSettingFailed, } From ceae51fe322ebf444d0c8b61fdb7a86ab11c8071 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Dec 2017 19:46:48 -0600 Subject: [PATCH 005/125] Make label optional and make sure the input field still renders properly without it --- .../website/ts/components/inputs/token_amount_input.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index f39341a4fc..1ae9bc85eb 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -8,9 +8,9 @@ import {BalanceBoundedInput} from 'ts/components/inputs/balance_bounded_input'; import {InputErrMsg, Token, TokenState, ValidatedBigNumberCallback, WebsitePaths} from 'ts/types'; interface TokenAmountInputProps { - label: string; token: Token; tokenState: TokenState; + label?: string; amount?: BigNumber; shouldShowIncompleteErrs: boolean; shouldCheckBalance: boolean; @@ -26,8 +26,9 @@ export class TokenAmountInput extends React.Component +
-
+
{this.props.token.symbol}
From 105bcc6664f0d4ab642486977f1edaf8d84b020e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Dec 2017 19:58:33 -0600 Subject: [PATCH 006/125] Clear trackedTokens so that a user starts tracking the new WETH and no longer the old one --- packages/website/ts/index.tsx | 2 ++ .../website/ts/local_storage/tracked_token_storage.ts | 11 +++++++++++ packages/website/ts/utils/configs.ts | 1 + 3 files changed, 14 insertions(+) diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 922102d969..61cd6c2f5e 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -10,6 +10,7 @@ import {BrowserRouter as Router, Link, Redirect, Route, Switch} from 'react-rout import * as injectTapEventPlugin from 'react-tap-event-plugin'; import {createStore, Store as ReduxStore} from 'redux'; import {createLazyComponent} from 'ts/lazy_component'; +import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage'; import {About} from 'ts/pages/about/about'; import {FAQ} from 'ts/pages/faq/faq'; @@ -29,6 +30,7 @@ BigNumber.config({ // Check if we've introduced an update that requires us to clear the tradeHistory local storage entries tradeHistoryStorage.clearIfRequired(); +trackedTokenStorage.clearIfRequired(); const CUSTOM_GREY = 'rgb(39, 39, 39)'; const CUSTOM_GREEN = 'rgb(102, 222, 117)'; diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts index 051a78ae1b..086b06af57 100644 --- a/packages/website/ts/local_storage/tracked_token_storage.ts +++ b/packages/website/ts/local_storage/tracked_token_storage.ts @@ -1,10 +1,21 @@ import * as _ from 'lodash'; import {localStorage} from 'ts/local_storage/local_storage'; import {Token, TrackedTokensByNetworkId} from 'ts/types'; +import {configs} from 'ts/utils/configs'; const TRACKED_TOKENS_KEY = 'trackedTokens'; +const TRACKED_TOKENS_CLEAR_KEY = 'lastClearTrackedTokensDate'; export const trackedTokenStorage = { + // Clear trackedTokens localStorage if we've updated the config variable in an update + // that introduced a backward incompatible change requiring the tracked tokens to be re-set + clearIfRequired() { + const lastClearFillDate = localStorage.getItemIfExists(TRACKED_TOKENS_CLEAR_KEY); + if (lastClearFillDate !== configs.lastLocalStorageTrackedTokenClearanceDate) { + localStorage.removeItem(TRACKED_TOKENS_KEY); + } + localStorage.setItem(TRACKED_TOKENS_CLEAR_KEY, configs.lastLocalStorageTrackedTokenClearanceDate); + }, addTrackedTokenToUser(userAddress: string, networkId: number, token: Token) { const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); let trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index e84dbd38fc..c887d176fa 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -14,6 +14,7 @@ export const configs = { // WARNING: ZRX & WETH MUST always be default trackedTokens defaultTrackedTokenSymbols: ['WETH', 'ZRX'], lastLocalStorageFillClearanceDate: '2017-11-22', + lastLocalStorageTrackedTokenClearanceDate: '2017-12-13', isMainnetEnabled: true, outdatedWrappedEthers: [ { From 7ce1021798ac2d459f80d1827a3259e781b4b201 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Dec 2017 10:41:33 -0600 Subject: [PATCH 007/125] Refactor the WETH conversion dialog/button to be either wrap or unwrap specific and not both --- .../dialogs/eth_weth_conversion_dialog.tsx | 130 +++++++++++------- .../components/eth_weth_conversion_button.tsx | 38 ++++- 2 files changed, 109 insertions(+), 59 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 aba5b9faf8..4904ee9e23 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -1,14 +1,15 @@ import BigNumber from 'bignumber.js'; import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; -import RadioButton from 'material-ui/RadioButton'; -import RadioButtonGroup from 'material-ui/RadioButton/RadioButtonGroup'; import * as React from 'react'; 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'; +const DARK_BLUE = '#4D5481'; + interface EthWethConversionDialogProps { + direction: Side; onComplete: (direction: Side, value: BigNumber) => void; onCancelled: () => void; isOpen: boolean; @@ -19,7 +20,6 @@ interface EthWethConversionDialogProps { interface EthWethConversionDialogState { value?: BigNumber; - direction: Side; shouldShowIncompleteErrs: boolean; hasErrors: boolean; } @@ -29,7 +29,6 @@ export class EthWethConversionDialog extends constructor() { super(); this.state = { - direction: Side.deposit, shouldShowIncompleteErrs: false, hasErrors: true, }; @@ -48,11 +47,13 @@ export class EthWethConversionDialog extends onTouchTap={this.onConvertClick.bind(this)} />, ]; + const title = this.props.direction === Side.deposit ? 'Wrap ETH' : 'Unwrap WETH'; return ( {this.renderConversionDialogBody()} @@ -60,56 +61,81 @@ export class EthWethConversionDialog extends ); } private renderConversionDialogBody() { + const explanation = this.props.direction === Side.deposit ? + 'Convert your Ether into a tokenized, tradable form.' : + 'Convert your Wrapped Ether back into it\'s native form.'; + const isWrappedVersion = this.props.direction === Side.receive; return ( -
- - - - - {this.state.direction === Side.receive ? - : - - } +
+
+ {explanation} +
+
+
+ {this.renderCurrency(isWrappedVersion)} +
+ +
+ {this.renderCurrency(!isWrappedVersion)} +
+
+ {this.props.direction === Side.receive ? + : + + } +
+ 1 ETH = 1 WETH +
+
+
); } - private onConversionDirectionChange(e: any, direction: Side) { - this.setState({ - value: undefined, - shouldShowIncompleteErrs: false, - direction, - hasErrors: true, - }); + private renderCurrency(isWrappedVersion: boolean) { + const name = isWrappedVersion ? 'Wrapped Ether' : 'Ether'; + const iconUrl = isWrappedVersion ? '/images/token_icons/ether_erc20.png' : '/images/ether.png'; + const symbol = isWrappedVersion ? 'WETH' : 'ETH'; + return ( +
+
+ {name} +
+
+ +
+
+ ({symbol}) +
+
+ ); } private onValueChange(isValid: boolean, amount?: BigNumber) { this.setState({ @@ -127,7 +153,7 @@ export class EthWethConversionDialog extends this.setState({ value: undefined, }); - this.props.onComplete(this.state.direction, value); + this.props.onComplete(this.props.direction, value); } } private onCancel() { diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index a83b1543fd..bf686d44b5 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -12,12 +12,15 @@ import {errorReporter} from 'ts/utils/error_reporter'; import {utils} from 'ts/utils/utils'; interface EthWethConversionButtonProps { + direction: Side; ethToken: Token; ethTokenState: TokenState; dispatcher: Dispatcher; blockchain: Blockchain; userEtherBalance: BigNumber; - onError: () => void; + isOutdatedWrappedEther: boolean; + onConversionSuccessful?: () => void; + isDisabled?: boolean; } interface EthWethConversionButtonState { @@ -27,6 +30,9 @@ interface EthWethConversionButtonState { export class EthWethConversionButton extends React.Component { + public static defaultProps: Partial = { + isDisabled: false, + }; public constructor(props: EthWethConversionButtonProps) { super(props); this.state = { @@ -36,16 +42,26 @@ export class EthWethConversionButton extends } public render() { const labelStyle = this.state.isEthConversionHappening ? {fontSize: 10} : {}; + let callToActionLabel; + let inProgressLabel; + if (this.props.direction === Side.deposit) { + callToActionLabel = 'Wrap'; + inProgressLabel = 'Wrapping...'; + } else { + callToActionLabel = 'Unwrap'; + inProgressLabel = 'Unwrapping...'; + } return (
Date: Thu, 14 Dec 2017 10:43:20 -0600 Subject: [PATCH 008/125] Use new wrap/unwrap buttons, fetch outdated WETH balances and re-fetch after a successful unwrap. --- .../website/ts/components/eth_wrappers.tsx | 249 +++++++++++++----- packages/website/ts/types.ts | 8 +- packages/website/ts/utils/configs.ts | 23 +- 3 files changed, 208 insertions(+), 72 deletions(-) diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 2779298706..c2f9b7b455 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -16,12 +16,16 @@ import { import * as moment from 'moment'; import * as React from 'react'; import {Blockchain} from 'ts/blockchain'; +import {EthWethConversionButton} from 'ts/components/eth_weth_conversion_button'; import {LifeCycleRaisedButton} from 'ts/components/ui/lifecycle_raised_button'; import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; import {Dispatcher} from 'ts/redux/dispatcher'; import { - OutdatedWrappedEther, + OutdatedWrappedEtherByNetworkId, + Side, + Token, TokenByAddress, + TokenState, TokenStateByAddress, } from 'ts/types'; import {configs} from 'ts/utils/configs'; @@ -31,11 +35,19 @@ import {utils} from 'ts/utils/utils'; const PRECISION = 5; const DATE_FORMAT = 'D/M/YY'; +const LIGHT_GRAY = '#A5A5A5'; const ICON_DIMENSION = 40; const ETHER_ICON_PATH = '/images/ether.png'; const OUTDATED_WETH_ICON_PATH = '/images/wrapped_eth_gray.png'; const ETHER_TOKEN_SYMBOL = 'WETH'; +interface OutdatedWETHAddressToIsStateLoaded { + [address: string]: boolean; +} +interface OutdatedWETHStateByAddress { + [address: string]: TokenState; +} + interface EthWrappersProps { networkId: number; blockchain: Blockchain; @@ -46,24 +58,58 @@ interface EthWrappersProps { userEtherBalance: BigNumber; } -interface EthWrappersState {} +interface EthWrappersState { + outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded; + outdatedWETHStateByAddress: OutdatedWETHStateByAddress; +} export class EthWrappers extends React.Component { constructor(props: EthWrappersProps) { super(props); - this.state = {}; + const outdatedWETHAddresses = this.getOutdatedWETHAddresses(); + const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; + const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; + _.each(outdatedWETHAddresses, outdatedWETHAddress => { + outdatedWETHAddressToIsStateLoaded[outdatedWETHAddress] = false; + outdatedWETHStateByAddress[outdatedWETHAddress] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + }; + }); + this.state = { + outdatedWETHAddressToIsStateLoaded, + outdatedWETHStateByAddress, + }; } public componentDidMount() { window.scrollTo(0, 0); + // tslint:disable-next-line:no-floating-promises + this.fetchOutdatedWETHStateAsync(); } public render() { const tokens = _.values(this.props.tokenByAddress); - const wethToken = _.find(tokens, {symbol: 'WETH'}); - const wethState = this.props.tokenStateByAddress[wethToken.address]; - const wethBalance = ZeroEx.toUnitAmount(wethState.balance, 18); + const etherToken = _.find(tokens, {symbol: 'WETH'}); + const etherTokenState = this.props.tokenStateByAddress[etherToken.address]; + const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, 18); return (
-

ETH Wrapper

+
+

ETH Wrapper

+ +
@@ -100,11 +146,14 @@ export class EthWrappers extends React.Component - @@ -124,11 +173,14 @@ export class EthWrappers extends React.Component - @@ -136,46 +188,66 @@ export class EthWrappers extends React.Component
-

Outdated WETH

- -
- The{' '} - - canonical WETH - contract is updated when necessary. - Unwrap outdated WETH in order to
 retrieve your ETH and move it - to the updated WETH token. -
- - - - WETH Version - Balance - - {'WETH -> ETH'} - - - - - {this.renderOutdatedWeths()} - -
+

Outdated WETH

+ +
+ The{' '} + + canonical WETH + contract is updated when necessary. + Unwrap outdated WETH in order to
 retrieve your ETH and move it + to the updated WETH token. +
+
+ + + + WETH Version + Balance + + {'WETH -> ETH'} + + + + + {this.renderOutdatedWeths(etherToken, etherTokenState)} + +
+
); } - private renderOutdatedWeths() { - const rows = _.map(configs.outdatedWrappedEthers, (outdatedWETH: OutdatedWrappedEther) => { - const timestampMsRange = outdatedWETH.timestampMsRangeByNetworkId[this.props.networkId]; - const startMoment = moment(timestampMsRange.startTimestampMs); - const endMoment = moment(timestampMsRange.endTimestampMs); + private renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) { + const rows = _.map(configs.outdatedWrappedEthers, + (outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => { + const outdatedWETH = outdatedWETHByNetworkId[this.props.networkId]; + const timestampMsRange = outdatedWETH.timestampMsRange; + let dateRange: string; + if (!_.isUndefined(timestampMsRange)) { + const startMoment = moment(timestampMsRange.startTimestampMs); + const endMoment = moment(timestampMsRange.endTimestampMs); + dateRange = `${startMoment.format(DATE_FORMAT)}-${endMoment.format(DATE_FORMAT)}`; + } else { + dateRange = '-'; + } + const outdatedEtherToken = { + ...etherToken, + address: outdatedWETH.address, + }; + const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETH.address]; + const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETH.address]; + const balanceInEthIfExists = isStateLoaded ? + ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, 18).toFixed(PRECISION) : + undefined; + const onConversionSuccessful = this.onOutdatedConversionSuccessfulAsync.bind(this, outdatedWETH.address); return ( @@ -185,19 +257,27 @@ export class EthWrappers extends React.Component
- {startMoment.format(DATE_FORMAT)}-{endMoment.format(DATE_FORMAT)} + {dateRange}
- 0 WETH + {isStateLoaded ? + `${balanceInEthIfExists} WETH` : + + } - @@ -205,10 +285,53 @@ export class EthWrappers extends React.Component { + return outdatedWrappedEther[this.props.networkId].address; + }); + return outdatedWETHAddresses; } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 4caa090259..2005cf2655 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -687,9 +687,11 @@ export interface TimestampMsRange { endTimestampMs: number; } -export interface OutdatedWrappedEther { - address: string; - timestampMsRangeByNetworkId: {[networkId: number]: TimestampMsRange}; +export interface OutdatedWrappedEtherByNetworkId { + [networkId: number]: { + address: string; + timestampMsRange: TimestampMsRange; + }; } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index c887d176fa..adc51374f3 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -1,5 +1,9 @@ import * as _ from 'lodash'; -import {Environments, OutdatedWrappedEther} from 'ts/types'; +import { + Environments, + OutdatedWrappedEtherByNetworkId, + TimestampMsRange, +} from 'ts/types'; const BASE_URL = window.location.origin; const isDevelopment = _.includes(BASE_URL, 'https://0xproject.dev:3572') || @@ -18,13 +22,20 @@ export const configs = { isMainnetEnabled: true, outdatedWrappedEthers: [ { - address: '0x05d090b51c40b020eab3bfcb6a2dff130df22e9c', - timestampMsRangeByNetworkId: { - 42: { + 42: { + address: '0x05d090b51c40b020eab3bfcb6a2dff130df22e9c', + timestampMsRange: { startTimestampMs: 1501614680000, endTimestampMs: 1513106129000, }, }, - } as OutdatedWrappedEther, - ], + 1: { + address: '0x2956356cd2a2bf3202f771f50d3d14a367b48070', + timestampMsRange: { + startTimestampMs: 1513123415000, + endTimestampMs: 1513106129000, + }, + }, + }, + ] as OutdatedWrappedEtherByNetworkId[], }; From ac1fbeb9623682ce678990d1815fcff63a6c40ae Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Dec 2017 19:28:49 -0600 Subject: [PATCH 009/125] Improve WETH page styling --- .../dialogs/eth_weth_conversion_dialog.tsx | 8 ++-- .../website/ts/components/eth_wrappers.tsx | 39 +++++++++++++++---- 2 files changed, 35 insertions(+), 12 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 4904ee9e23..c8bdced9bd 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -53,7 +53,7 @@ export class EthWethConversionDialog extends title={title} titleStyle={{fontWeight: 100}} actions={convertDialogActions} - contentStyle={{width: 600}} + contentStyle={{width: 448}} open={this.props.isOpen} > {this.renderConversionDialogBody()} @@ -70,7 +70,7 @@ export class EthWethConversionDialog extends
{explanation}
-
+
{this.renderCurrency(isWrappedVersion)}
@@ -83,7 +83,7 @@ export class EthWethConversionDialog extends
{this.props.direction === Side.receive ?
-
+
({symbol})
diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index c2f9b7b455..ccbed41882 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -91,6 +91,7 @@ export class EthWrappers extends React.Component
@@ -113,7 +114,7 @@ export class EthWrappers extends React.Component
- Wrap ETH into an ERC20-compliant Ether token + Wrap ETH into an ERC20-compliant Ether token. 1 ETH = 1 WETH.
ETH TokenBalance - - {'ETH <-> WETH'} + + {this.renderActionColumnTitle(isBidirectional)} @@ -145,7 +146,7 @@ export class EthWrappers extends React.Component {this.props.userEtherBalance.toFixed(PRECISION)} ETH - + {wethBalance.toFixed(PRECISION)} WETH - + WETH Version Balance - - {'WETH -> ETH'} + + {this.renderActionColumnTitle(!isBidirectional)} @@ -225,6 +226,28 @@ export class EthWrappers extends React.Component ); } + private renderActionColumnTitle(isBidirectional: boolean) { + let iconClass = 'zmdi-long-arrow-right'; + let leftSymbol = 'WETH'; + let rightSymbol = 'ETH'; + if (isBidirectional) { + iconClass = 'zmdi-swap'; + leftSymbol = 'ETH'; + rightSymbol = 'WETH'; + } + return ( +
+
{leftSymbol}
+
+ +
+
{rightSymbol}
+
+ ); + } private renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) { const rows = _.map(configs.outdatedWrappedEthers, (outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => { @@ -267,7 +290,7 @@ export class EthWrappers extends React.Component }
- + Date: Fri, 15 Dec 2017 13:14:44 +0100 Subject: [PATCH 010/125] Dissallow unused vars/imports --- packages/tslint-config/tslint.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/tslint-config/tslint.json b/packages/tslint-config/tslint.json index d3ee51a63c..91185bddbb 100644 --- a/packages/tslint-config/tslint.json +++ b/packages/tslint-config/tslint.json @@ -51,6 +51,7 @@ "no-string-throw": true, "no-submodule-imports": false, "no-unnecessary-type-assertion": true, + "no-unused-variable": [true, {"ignore-pattern": "^_\\d*"}], "no-implicit-dependencies": [true, "dev"], "number-literal-format": true, "object-literal-sort-keys": false, From 274aa50d740e4f3af9e3d50468843ed19b555aa3 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 15 Dec 2017 13:14:57 +0100 Subject: [PATCH 011/125] Fix 0x.js unused vars --- .../contract_templates/contract.mustache | 1 + packages/0x.js/src/0x.ts | 2 -- .../src/contract_wrappers/exchange_wrapper.ts | 5 +--- .../token_registry_wrapper.ts | 4 +-- .../token_transfer_proxy_wrapper.ts | 1 - .../src/contract_wrappers/token_wrapper.ts | 2 -- .../0x.js/src/order_watcher/event_watcher.ts | 3 --- .../src/order_watcher/expiration_watcher.ts | 3 +-- .../src/order_watcher/order_state_watcher.ts | 2 -- .../balance_proxy_allowance_lazy_store.ts | 1 - .../order_filled_cancelled_lazy_store.ts | 1 - packages/0x.js/src/utils/assert.ts | 10 +++---- packages/0x.js/src/utils/order_state_utils.ts | 5 ---- .../0x.js/src/utils/order_validation_utils.ts | 5 +--- packages/0x.js/test/0x.js_test.ts | 2 +- packages/0x.js/test/artifacts_test.ts | 2 -- packages/0x.js/test/event_watcher_test.ts | 5 ---- packages/0x.js/test/exchange_wrapper_test.ts | 1 - .../0x.js/test/expiration_watcher_test.ts | 1 - .../0x.js/test/order_state_watcher_test.ts | 27 ------------------- packages/0x.js/test/order_validation_test.ts | 5 ---- .../remaining_fillable_calculator_test.ts | 1 - packages/0x.js/test/subscription_test.ts | 8 +----- .../test/token_transfer_proxy_wrapper_test.ts | 1 - packages/0x.js/test/token_wrapper_test.ts | 9 +------ 25 files changed, 13 insertions(+), 94 deletions(-) diff --git a/packages/0x.js/contract_templates/contract.mustache b/packages/0x.js/contract_templates/contract.mustache index 4c59d4f585..693fbe4ee5 100644 --- a/packages/0x.js/contract_templates/contract.mustache +++ b/packages/0x.js/contract_templates/contract.mustache @@ -2,6 +2,7 @@ * 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 {classUtils, promisify} from '@0xproject/utils'; import {BigNumber} from 'bignumber.js'; diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts index 83d4708458..5a2d6cb059 100644 --- a/packages/0x.js/src/0x.ts +++ b/packages/0x.js/src/0x.ts @@ -16,7 +16,6 @@ import {zeroExConfigSchema} from './schemas/zero_ex_config_schema'; import { ECSignature, Order, - OrderStateWatcherConfig, SignedOrder, TransactionReceiptWithDecodedLogs, Web3Provider, @@ -26,7 +25,6 @@ import { import {AbiDecoder} from './utils/abi_decoder'; import {assert} from './utils/assert'; import {constants} from './utils/constants'; -import {OrderStateUtils} from './utils/order_state_utils'; import {signatureUtils} from './utils/signature_utils'; import {utils} from './utils/utils'; diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts index 1e9865395b..9bed400799 100644 --- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts @@ -15,9 +15,7 @@ import { ExchangeContractEventArgs, ExchangeEvents, IndexedFilterValues, - LogCancelContractEventArgs, LogErrorContractEventArgs, - LogFillContractEventArgs, LogWithDecodedArgs, MethodOpts, Order, @@ -26,7 +24,6 @@ import { OrderFillRequest, OrderTransactionOpts, OrderValues, - RawLog, SignedOrder, SubscriptionOpts, ValidateOrderFillableOpts, @@ -88,7 +85,7 @@ export class ExchangeWrapper extends ContractWrapper { tokenWrapper: TokenWrapper, contractAddressIfExists?: string) { super(web3Wrapper, networkId, abiDecoder); this._tokenWrapper = tokenWrapper; - this._orderValidationUtils = new OrderValidationUtils(tokenWrapper, this); + this._orderValidationUtils = new OrderValidationUtils(this); this._contractAddressIfExists = contractAddressIfExists; } /** diff --git a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts index 064b826d88..69e9d7e21c 100644 --- a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts @@ -2,7 +2,7 @@ import {Web3Wrapper} from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import {artifacts} from '../artifacts'; -import {Token, TokenMetadata, ZeroExError} from '../types'; +import {Token, TokenMetadata} from '../types'; import {assert} from '../utils/assert'; import {constants} from '../utils/constants'; @@ -36,8 +36,6 @@ export class TokenRegistryWrapper extends ContractWrapper { * @return An array of objects that conform to the Token interface. */ public async getTokensAsync(): Promise { - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const addresses = await this.getTokenAddressesAsync(); const tokenPromises: Array> = _.map( addresses, diff --git a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts index 1a16e35402..c67ef87dff 100644 --- a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts @@ -2,7 +2,6 @@ import {Web3Wrapper} from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import {artifacts} from '../artifacts'; -import {ZeroExError} from '../types'; import {ContractWrapper} from './contract_wrapper'; import {TokenTransferProxyContract} from './generated/token_transfer_proxy'; diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts index 684f291a53..d1553fa7b1 100644 --- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts @@ -23,8 +23,6 @@ import {ContractWrapper} from './contract_wrapper'; import {TokenContract} from './generated/token'; import {TokenTransferProxyWrapper} from './token_transfer_proxy_wrapper'; -const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 47275; - /** * This class includes all the functionality related to interacting with ERC20 token contracts. * All ERC20 method calls are supported, along with some convenience methods for getting/setting allowances diff --git a/packages/0x.js/src/order_watcher/event_watcher.ts b/packages/0x.js/src/order_watcher/event_watcher.ts index c110792086..197dfae659 100644 --- a/packages/0x.js/src/order_watcher/event_watcher.ts +++ b/packages/0x.js/src/order_watcher/event_watcher.ts @@ -5,13 +5,10 @@ import * as Web3 from 'web3'; import { BlockParamLiteral, - EventCallback, EventWatcherCallback, ZeroExError, } from '../types'; -import {AbiDecoder} from '../utils/abi_decoder'; import {assert} from '../utils/assert'; -import {utils} from '../utils/utils'; const DEFAULT_EVENT_POLLING_INTERVAL_MS = 200; diff --git a/packages/0x.js/src/order_watcher/expiration_watcher.ts b/packages/0x.js/src/order_watcher/expiration_watcher.ts index 5faac43cfc..91e8151fdc 100644 --- a/packages/0x.js/src/order_watcher/expiration_watcher.ts +++ b/packages/0x.js/src/order_watcher/expiration_watcher.ts @@ -3,8 +3,7 @@ import {BigNumber} from 'bignumber.js'; import {RBTree} from 'bintrees'; import * as _ from 'lodash'; -import {ZeroEx} from '../0x'; -import {SignedOrder, ZeroExError} from '../types'; +import {ZeroExError} from '../types'; import {utils} from '../utils/utils'; const DEFAULT_EXPIRATION_MARGIN_MS = 0; diff --git a/packages/0x.js/src/order_watcher/order_state_watcher.ts b/packages/0x.js/src/order_watcher/order_state_watcher.ts index 9ae1579c6e..c4a9eb1ad0 100644 --- a/packages/0x.js/src/order_watcher/order_state_watcher.ts +++ b/packages/0x.js/src/order_watcher/order_state_watcher.ts @@ -4,7 +4,6 @@ import {Web3Wrapper} from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import {ZeroEx} from '../0x'; -import {artifacts} from '../artifacts'; import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; @@ -25,7 +24,6 @@ import { SignedOrder, TokenEvents, TransferContractEventArgs, - Web3Provider, ZeroExError, } from '../types'; import {AbiDecoder} from '../utils/abi_decoder'; diff --git a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts index 6225e9e724..cde1ef095d 100644 --- a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts +++ b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts @@ -1,6 +1,5 @@ import {BigNumber} from 'bignumber.js'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {BlockParamLiteral} from '../types'; diff --git a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts index 28b32f9e2c..b35355e38c 100644 --- a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts +++ b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts @@ -1,6 +1,5 @@ import {BigNumber} from 'bignumber.js'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; import {BlockParamLiteral} from '../types'; diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts index 4cf6caf775..86a6a7c010 100644 --- a/packages/0x.js/src/utils/assert.ts +++ b/packages/0x.js/src/utils/assert.ts @@ -1,15 +1,15 @@ import {assert as sharedAssert} from '@0xproject/assert'; -import {Schema, SchemaValidator} from '@0xproject/json-schemas'; +// We need those two unused imports because they're actually used by sharedAssert which gets injected here +// tslint:disable-next-line:no-unused-variable +import {Schema} from '@0xproject/json-schemas'; import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import BigNumber from 'bignumber.js'; +// tslint:disable-next-line:no-unused-variable +import * as BigNumber from 'bignumber.js'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; import {ECSignature} from '../types'; import {signatureUtils} from '../utils/signature_utils'; -const HEX_REGEX = /^0x[0-9A-F]*$/i; - export const assert = { ...sharedAssert, isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) { diff --git a/packages/0x.js/src/utils/order_state_utils.ts b/packages/0x.js/src/utils/order_state_utils.ts index 6b7f811aea..a0985d028f 100644 --- a/packages/0x.js/src/utils/order_state_utils.ts +++ b/packages/0x.js/src/utils/order_state_utils.ts @@ -1,24 +1,19 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; import {ZeroEx} from '../0x'; import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {RemainingFillableCalculator} from '../order_watcher/remaining_fillable_calculator'; import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store'; import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store'; import { ExchangeContractErrs, - MethodOpts, OrderRelevantState, OrderState, OrderStateInvalid, OrderStateValid, SignedOrder, } from '../types'; -import {constants} from '../utils/constants'; -import {utils} from '../utils/utils'; const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001; diff --git a/packages/0x.js/src/utils/order_validation_utils.ts b/packages/0x.js/src/utils/order_validation_utils.ts index d514483e07..9185eef2b6 100644 --- a/packages/0x.js/src/utils/order_validation_utils.ts +++ b/packages/0x.js/src/utils/order_validation_utils.ts @@ -3,7 +3,6 @@ import * as _ from 'lodash'; import {ZeroEx} from '../0x'; import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper'; -import {TokenWrapper} from '../contract_wrappers/token_wrapper'; import {ExchangeContractErrs, Order, SignedOrder, TradeSide, TransferType, ZeroExError} from '../types'; import {constants} from '../utils/constants'; import {utils} from '../utils/utils'; @@ -11,7 +10,6 @@ import {utils} from '../utils/utils'; import {ExchangeTransferSimulator} from './exchange_transfer_simulator'; export class OrderValidationUtils { - private tokenWrapper: TokenWrapper; private exchangeWrapper: ExchangeWrapper; public static validateCancelOrderThrowIfInvalid( order: Order, cancelTakerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber, @@ -84,8 +82,7 @@ export class OrderValidationUtils { .round(0); return fillMakerTokenAmount; } - constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) { - this.tokenWrapper = tokenWrapper; + constructor(exchangeWrapper: ExchangeWrapper) { this.exchangeWrapper = exchangeWrapper; } public async validateOrderFillableOrThrowAsync( diff --git a/packages/0x.js/test/0x.js_test.ts b/packages/0x.js/test/0x.js_test.ts index 52f9566dc1..e1ceb12c85 100644 --- a/packages/0x.js/test/0x.js_test.ts +++ b/packages/0x.js/test/0x.js_test.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import 'mocha'; import * as Sinon from 'sinon'; -import {ApprovalContractEventArgs, LogWithDecodedArgs, Order, TokenEvents, ZeroEx, ZeroExError} from '../src'; +import {ApprovalContractEventArgs, LogWithDecodedArgs, Order, TokenEvents, ZeroEx} from '../src'; import {chaiSetup} from './utils/chai_setup'; import {constants} from './utils/constants'; diff --git a/packages/0x.js/test/artifacts_test.ts b/packages/0x.js/test/artifacts_test.ts index f4599a24d6..d12346db32 100644 --- a/packages/0x.js/test/artifacts_test.ts +++ b/packages/0x.js/test/artifacts_test.ts @@ -1,4 +1,3 @@ -import * as chai from 'chai'; import * as fs from 'fs'; import HDWalletProvider = require('truffle-hdwallet-provider'); @@ -8,7 +7,6 @@ import {chaiSetup} from './utils/chai_setup'; import {constants} from './utils/constants'; chaiSetup.configure(); -const expect = chai.expect; // Those tests are slower cause they're talking to a remote node const TIMEOUT = 10000; diff --git a/packages/0x.js/test/event_watcher_test.ts b/packages/0x.js/test/event_watcher_test.ts index 3d92c62a37..95af99268b 100644 --- a/packages/0x.js/test/event_watcher_test.ts +++ b/packages/0x.js/test/event_watcher_test.ts @@ -1,5 +1,4 @@ import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import BigNumber from 'bignumber.js'; import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; @@ -7,15 +6,12 @@ import * as Sinon from 'sinon'; import * as Web3 from 'web3'; import { - DecodedLogEvent, LogEvent, - ZeroEx, } from '../src'; import {EventWatcher} from '../src/order_watcher/event_watcher'; import {DoneCallback} from '../src/types'; import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; import {web3Factory} from './utils/web3_factory'; chaiSetup.configure(); @@ -26,7 +22,6 @@ describe('EventWatcher', () => { let stubs: Sinon.SinonStub[] = []; let eventWatcher: EventWatcher; let web3Wrapper: Web3Wrapper; - const numConfirmations = 0; const logA: Web3.LogEntry = { address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5', blockHash: null, diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts index dea9610265..51b511dbf6 100644 --- a/packages/0x.js/test/exchange_wrapper_test.ts +++ b/packages/0x.js/test/exchange_wrapper_test.ts @@ -9,7 +9,6 @@ import { ExchangeContractErrs, ExchangeEvents, LogCancelContractEventArgs, - LogEvent, LogFillContractEventArgs, OrderCancellationRequest, OrderFillRequest, diff --git a/packages/0x.js/test/expiration_watcher_test.ts b/packages/0x.js/test/expiration_watcher_test.ts index c1618ce9da..30c395f008 100644 --- a/packages/0x.js/test/expiration_watcher_test.ts +++ b/packages/0x.js/test/expiration_watcher_test.ts @@ -1,5 +1,4 @@ import {BlockchainLifecycle} from '@0xproject/dev-utils'; -import {Web3Wrapper} from '@0xproject/web3-wrapper'; import BigNumber from 'bignumber.js'; import * as chai from 'chai'; import * as _ from 'lodash'; diff --git a/packages/0x.js/test/order_state_watcher_test.ts b/packages/0x.js/test/order_state_watcher_test.ts index 99e6646a34..4d9a96502c 100644 --- a/packages/0x.js/test/order_state_watcher_test.ts +++ b/packages/0x.js/test/order_state_watcher_test.ts @@ -1,5 +1,4 @@ import {BlockchainLifecycle} from '@0xproject/dev-utils'; -import {Web3Wrapper} from '@0xproject/web3-wrapper'; import BigNumber from 'bignumber.js'; import * as chai from 'chai'; import * as _ from 'lodash'; @@ -7,19 +6,15 @@ import 'mocha'; import * as Web3 from 'web3'; import { - DecodedLogEvent, ExchangeContractErrs, - LogEvent, OrderState, OrderStateInvalid, OrderStateValid, SignedOrder, Token, ZeroEx, - ZeroExConfig, ZeroExError, } from '../src'; -import {OrderStateWatcher} from '../src/order_watcher/order_state_watcher'; import {DoneCallback} from '../src/types'; import {chaiSetup} from './utils/chai_setup'; @@ -48,7 +43,6 @@ describe('OrderStateWatcher', () => { let takerToken: Token; let maker: string; let taker: string; - let web3Wrapper: Web3Wrapper; let signedOrder: SignedOrder; const config = { networkId: constants.TESTRPC_NETWORK_ID, @@ -67,7 +61,6 @@ describe('OrderStateWatcher', () => { fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); await fillScenarios.initTokenBalancesAsync(); [makerToken, takerToken] = tokenUtils.getDummyTokens(); - web3Wrapper = (zeroEx as any)._web3Wrapper; }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -142,7 +135,6 @@ describe('OrderStateWatcher', () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerToken.address, takerToken.address, maker, taker, fillableAmount, ); - const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); const callback = reportCallbackErrors(done)((orderState: OrderState) => { throw new Error('OrderState callback fired for irrelevant order'); @@ -151,7 +143,6 @@ describe('OrderStateWatcher', () => { const notTheMaker = userAddresses[0]; const anyRecipient = taker; const transferAmount = new BigNumber(2); - const notTheMakerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, notTheMaker); await zeroEx.token.transferAsync(makerToken.address, notTheMaker, anyRecipient, transferAmount); setTimeout(() => { done(); @@ -208,8 +199,6 @@ describe('OrderStateWatcher', () => { ); const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); - const takerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, taker); - const fillAmountInBaseUnits = new BigNumber(2); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); @@ -242,8 +231,6 @@ describe('OrderStateWatcher', () => { signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( makerToken.address, takerToken.address, makerFee, takerFee, maker, taker, fillableAmount, taker); - const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); const callback = reportCallbackErrors(done)((orderState: OrderState) => { done(); }); @@ -260,8 +247,6 @@ describe('OrderStateWatcher', () => { makerToken.address, takerToken.address, maker, taker, makerFillableAmount, takerFillableAmount, ); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); - const takerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, taker); const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); @@ -289,8 +274,6 @@ describe('OrderStateWatcher', () => { makerToken.address, takerToken.address, maker, taker, fillableAmount, ); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); - const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals); zeroEx.orderStateWatcher.addOrder(signedOrder); @@ -343,8 +326,6 @@ describe('OrderStateWatcher', () => { makerToken.address, takerToken.address, makerFee, takerFee, maker, taker, fillableAmount, feeRecipient); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); - const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals); const transferTokenAmount = makerFee.sub(remainingTokenAmount); zeroEx.orderStateWatcher.addOrder(signedOrder); @@ -370,10 +351,7 @@ describe('OrderStateWatcher', () => { makerToken.address, takerToken.address, makerFee, takerFee, maker, taker, fillableAmount, feeRecipient); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); - const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals); - const transferFeeAmount = makerFee.sub(remainingFeeAmount); const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals); const transferTokenAmount = makerFee.sub(remainingTokenAmount); @@ -401,7 +379,6 @@ describe('OrderStateWatcher', () => { makerToken.address, takerToken.address, makerFee, takerFee, maker, taker, fillableAmount, feeRecipient); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); zeroEx.orderStateWatcher.addOrder(signedOrder); const callback = reportCallbackErrors(done)((orderState: OrderState) => { @@ -434,7 +411,6 @@ describe('OrderStateWatcher', () => { }); zeroEx.orderStateWatcher.subscribe(callback); - const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); })().catch(done); }); @@ -466,9 +442,6 @@ describe('OrderStateWatcher', () => { makerToken.address, takerToken.address, maker, taker, fillableAmount, ); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); - const takerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, taker); - const cancelAmountInBaseUnits = new BigNumber(2); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); diff --git a/packages/0x.js/test/order_validation_test.ts b/packages/0x.js/test/order_validation_test.ts index c6d5d33927..b36a513fed 100644 --- a/packages/0x.js/test/order_validation_test.ts +++ b/packages/0x.js/test/order_validation_test.ts @@ -34,7 +34,6 @@ describe('OrderValidation', () => { let makerAddress: string; let takerAddress: string; let feeRecipient: string; - let orderValidationUtils: OrderValidationUtils; const fillableAmount = new BigNumber(5); const fillTakerAmount = new BigNumber(5); const config = { @@ -53,7 +52,6 @@ describe('OrderValidation', () => { const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; - orderValidationUtils = new OrderValidationUtils(zeroEx.token, zeroEx.exchange); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -181,7 +179,6 @@ describe('OrderValidation', () => { }); describe('validateCancelOrderAndThrowIfInvalidAsync', () => { let signedOrder: SignedOrder; - let orderHashHex: string; const cancelAmount = new BigNumber(3); beforeEach(async () => { [coinbase, makerAddress, takerAddress] = userAddresses; @@ -191,7 +188,6 @@ describe('OrderValidation', () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); - orderHashHex = ZeroEx.getOrderHashHex(signedOrder); }); it('should throw when cancel amount is zero', async () => { const zeroCancelAmount = new BigNumber(0); @@ -204,7 +200,6 @@ describe('OrderValidation', () => { makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast, ); - orderHashHex = ZeroEx.getOrderHashHex(expiredSignedOrder); return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount)) .to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired); }); diff --git a/packages/0x.js/test/remaining_fillable_calculator_test.ts b/packages/0x.js/test/remaining_fillable_calculator_test.ts index 610bf9b1aa..47ba13cd79 100644 --- a/packages/0x.js/test/remaining_fillable_calculator_test.ts +++ b/packages/0x.js/test/remaining_fillable_calculator_test.ts @@ -7,7 +7,6 @@ import { RemainingFillableCalculator } from '../src/order_watcher/remaining_fill import { ECSignature, SignedOrder } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; -import { TokenUtils } from './utils/token_utils'; chaiSetup.configure(); const expect = chai.expect; diff --git a/packages/0x.js/test/subscription_test.ts b/packages/0x.js/test/subscription_test.ts index d280d720a8..43e6b63b66 100644 --- a/packages/0x.js/test/subscription_test.ts +++ b/packages/0x.js/test/subscription_test.ts @@ -12,14 +12,12 @@ import { Token, TokenEvents, ZeroEx, - ZeroExError, } from '../src'; -import {BlockParamLiteral, DoneCallback} from '../src/types'; +import {DoneCallback} from '../src/types'; import {chaiSetup} from './utils/chai_setup'; import {constants} from './utils/constants'; import {reportCallbackErrors} from './utils/report_callback_errors'; -import {TokenUtils} from './utils/token_utils'; import {web3Factory} from './utils/web3_factory'; chaiSetup.configure(); @@ -31,7 +29,6 @@ describe('SubscriptionTest', () => { let zeroEx: ZeroEx; let userAddresses: string[]; let tokens: Token[]; - let tokenUtils: TokenUtils; let coinbase: string; let addressWithoutFunds: string; const config = { @@ -42,7 +39,6 @@ describe('SubscriptionTest', () => { zeroEx = new ZeroEx(web3.currentProvider, config); userAddresses = await zeroEx.getAvailableAddressesAsync(); tokens = await zeroEx.tokenRegistry.getTokensAsync(); - tokenUtils = new TokenUtils(tokens); coinbase = userAddresses[0]; addressWithoutFunds = userAddresses[1]; }); @@ -54,9 +50,7 @@ describe('SubscriptionTest', () => { }); describe('#subscribe', () => { const indexFilterValues = {}; - const shouldThrowOnInsufficientBalanceOrAllowance = true; let tokenAddress: string; - const transferAmount = new BigNumber(42); const allowanceAmount = new BigNumber(42); let stubs: Sinon.SinonStub[] = []; before(() => { diff --git a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts index 05861d112a..9674b64ac5 100644 --- a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts +++ b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts @@ -1,7 +1,6 @@ import * as chai from 'chai'; import {ZeroEx} from '../src'; -import {TokenTransferProxyWrapper} from '../src/contract_wrappers/token_transfer_proxy_wrapper'; import {chaiSetup} from './utils/chai_setup'; import {constants} from './utils/constants'; diff --git a/packages/0x.js/test/token_wrapper_test.ts b/packages/0x.js/test/token_wrapper_test.ts index 1ee2b25fcb..70637dbfed 100644 --- a/packages/0x.js/test/token_wrapper_test.ts +++ b/packages/0x.js/test/token_wrapper_test.ts @@ -1,5 +1,4 @@ import {BlockchainLifecycle} from '@0xproject/dev-utils'; -import {promisify} from '@0xproject/utils'; import {Web3Wrapper} from '@0xproject/web3-wrapper'; import BigNumber from 'bignumber.js'; import * as chai from 'chai'; @@ -8,13 +7,9 @@ import * as Web3 from 'web3'; import { ApprovalContractEventArgs, - ContractEvent, DecodedLogEvent, - LogEvent, - LogWithDecodedArgs, SubscriptionOpts, Token, - TokenContractEventArgs, TokenEvents, TransferContractEventArgs, ZeroEx, @@ -71,8 +66,7 @@ describe('TokenWrapper', () => { const toAddress = addressWithoutFunds; const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress); expect(preBalance).to.be.bignumber.equal(0); - const txHash = await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount); - const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); + await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount); const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress); return expect(postBalance).to.be.bignumber.equal(transferAmount); }); @@ -354,7 +348,6 @@ describe('TokenWrapper', () => { }); describe('#subscribe', () => { const indexFilterValues = {}; - const shouldThrowOnInsufficientBalanceOrAllowance = true; let tokenAddress: string; const transferAmount = new BigNumber(42); const allowanceAmount = new BigNumber(42); From 126048bac9f52871b841d9898cabe7cfd265ebb6 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 15 Dec 2017 13:58:30 +0100 Subject: [PATCH 012/125] Fix connect unused vars --- .../utils/orderbook_channel_message_parsers.ts | 1 - packages/connect/src/ws_orderbook_channel.ts | 2 -- packages/contracts/deploy/migrations/migrate.ts | 2 -- packages/contracts/deploy/src/compiler.ts | 2 -- packages/contracts/deploy/src/deployer.ts | 1 - packages/contracts/deploy/test/deploy_test.ts | 2 +- .../migrations/2_deploy_independent_contracts.ts | 2 +- .../contracts/migrations/3_register_tokens.ts | 2 +- packages/contracts/test/ts/ether_token_v2.ts | 5 ++--- packages/contracts/test/ts/exchange/core.ts | 1 - packages/contracts/test/ts/exchange/wrapper.ts | 4 +--- packages/contracts/util/crypto.ts | 1 - packages/dev-utils/src/rpc.ts | 1 - packages/json-schemas/test/schema_test.ts | 2 -- packages/kovan-faucets/src/ts/error_reporter.ts | 1 - packages/kovan-faucets/src/ts/utils.ts | 3 --- .../kovan-faucets/src/ts/zrx_request_queue.ts | 1 - packages/subproviders/src/subproviders/ledger.ts | 15 --------------- .../subproviders/src/subproviders/subprovider.ts | 4 ---- packages/subproviders/src/types.ts | 1 - .../test/integration/ledger_subprovider_test.ts | 4 ---- .../test/unit/ledger_subprovider_test.ts | 3 --- .../test/unit/redundant_rpc_subprovider_test.ts | 1 + .../tslint-config/rules/walkers/async_suffix.ts | 1 + 24 files changed, 8 insertions(+), 54 deletions(-) diff --git a/packages/connect/src/utils/orderbook_channel_message_parsers.ts b/packages/connect/src/utils/orderbook_channel_message_parsers.ts index 486a416ef2..d9a84cca21 100644 --- a/packages/connect/src/utils/orderbook_channel_message_parsers.ts +++ b/packages/connect/src/utils/orderbook_channel_message_parsers.ts @@ -5,7 +5,6 @@ import * as _ from 'lodash'; import { OrderbookChannelMessage, OrderbookChannelMessageTypes, - SignedOrder, } from '../types'; import {typeConverters} from './type_converters'; diff --git a/packages/connect/src/ws_orderbook_channel.ts b/packages/connect/src/ws_orderbook_channel.ts index 6687025c04..f585934848 100644 --- a/packages/connect/src/ws_orderbook_channel.ts +++ b/packages/connect/src/ws_orderbook_channel.ts @@ -8,7 +8,6 @@ import { OrderbookChannelHandler, OrderbookChannelMessageTypes, OrderbookChannelSubscriptionOpts, - SignedOrder, WebsocketClientEventType, WebsocketConnectionEventType, } from './types'; @@ -99,7 +98,6 @@ export class WebSocketOrderbookChannel implements OrderbookChannel { try { const utf8Data = message.utf8Data; const parserResult = orderbookChannelMessageParsers.parser(utf8Data); - const type = parserResult.type; if (parserResult.requestId === requestId) { switch (parserResult.type) { case (OrderbookChannelMessageTypes.Snapshot): { diff --git a/packages/contracts/deploy/migrations/migrate.ts b/packages/contracts/deploy/migrations/migrate.ts index f40b7c4ba6..e893e6a6f7 100644 --- a/packages/contracts/deploy/migrations/migrate.ts +++ b/packages/contracts/deploy/migrations/migrate.ts @@ -1,11 +1,9 @@ import {Web3Wrapper} from '@0xproject/web3-wrapper'; import {BigNumber} from 'bignumber.js'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; import {Deployer} from './../src/deployer'; import {constants} from './../src/utils/constants'; -import {Token} from './../src/utils/types'; import {tokenInfo} from './config/token_info'; export const migrator = { diff --git a/packages/contracts/deploy/src/compiler.ts b/packages/contracts/deploy/src/compiler.ts index 8a44e94a3e..333a2d0cea 100644 --- a/packages/contracts/deploy/src/compiler.ts +++ b/packages/contracts/deploy/src/compiler.ts @@ -1,4 +1,3 @@ -import {promisify} from '@0xproject/utils'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import * as path from 'path'; @@ -14,7 +13,6 @@ import { ContractNetworks, ContractSources, ImportContents, - SolcErrors, } from './utils/types'; import {utils} from './utils/utils'; diff --git a/packages/contracts/deploy/src/deployer.ts b/packages/contracts/deploy/src/deployer.ts index 4c8018ecc7..991504972f 100644 --- a/packages/contracts/deploy/src/deployer.ts +++ b/packages/contracts/deploy/src/deployer.ts @@ -1,5 +1,4 @@ import {TxData} from '@0xproject/types'; -import {promisify} from '@0xproject/utils'; import {Web3Wrapper} from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import * as Web3 from 'web3'; diff --git a/packages/contracts/deploy/test/deploy_test.ts b/packages/contracts/deploy/test/deploy_test.ts index 7e7b98f90b..263174a948 100644 --- a/packages/contracts/deploy/test/deploy_test.ts +++ b/packages/contracts/deploy/test/deploy_test.ts @@ -4,7 +4,7 @@ import 'mocha'; import {Compiler} from './../src/compiler'; import {Deployer} from './../src/deployer'; import {fsWrapper} from './../src/utils/fs_wrapper'; -import {CompilerOptions, ContractArtifact, ContractData, DeployerOptions, DoneCallback} from './../src/utils/types'; +import {CompilerOptions, ContractArtifact, ContractData, DoneCallback} from './../src/utils/types'; import {constructor_args, exchange_binary} from './fixtures/exchange_bin'; import {constants} from './util/constants'; diff --git a/packages/contracts/migrations/2_deploy_independent_contracts.ts b/packages/contracts/migrations/2_deploy_independent_contracts.ts index db9fc370d6..4bf316be6e 100644 --- a/packages/contracts/migrations/2_deploy_independent_contracts.ts +++ b/packages/contracts/migrations/2_deploy_independent_contracts.ts @@ -1,5 +1,5 @@ import {Artifacts} from '../util/artifacts'; -import {ContractInstance, MultiSigConfigByNetwork} from '../util/types'; +import {MultiSigConfigByNetwork} from '../util/types'; const { MultiSigWalletWithTimeLock, TokenTransferProxy, diff --git a/packages/contracts/migrations/3_register_tokens.ts b/packages/contracts/migrations/3_register_tokens.ts index c72ac15100..f816936283 100644 --- a/packages/contracts/migrations/3_register_tokens.ts +++ b/packages/contracts/migrations/3_register_tokens.ts @@ -3,7 +3,7 @@ import * as _ from 'lodash'; import {Artifacts} from '../util/artifacts'; import {constants} from '../util/constants'; -import {ContractInstance, Token, TokenInfoByNetwork} from '../util/types'; +import {ContractInstance, Token} from '../util/types'; import {tokenInfo} from './config/token_info'; const { diff --git a/packages/contracts/test/ts/ether_token_v2.ts b/packages/contracts/test/ts/ether_token_v2.ts index b88fef579c..61ac4cb578 100644 --- a/packages/contracts/test/ts/ether_token_v2.ts +++ b/packages/contracts/test/ts/ether_token_v2.ts @@ -81,7 +81,7 @@ contract('EtherTokenV2', (accounts: string[]) => { const logArgs = (logs[0] as any).args; expect(logArgs._from).to.equal(expectedFrom); expect(logArgs._to).to.equal(expectedTo); - expect(logArgs._value).to.be.bignumber.equal(ethToDeposit); + expect(logArgs._value).to.be.bignumber.equal(expectedValue); }); }); @@ -120,7 +120,6 @@ contract('EtherTokenV2', (accounts: string[]) => { it('should log 1 event with correct arguments', async () => { const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); - const initEthBalance = await getEthBalanceAsync(account); const ethTokensToWithdraw = initEthTokenBalance; expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0); const txHash = await zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account, { @@ -137,7 +136,7 @@ contract('EtherTokenV2', (accounts: string[]) => { const logArgs = (logs[0] as any).args; expect(logArgs._from).to.equal(expectedFrom); expect(logArgs._to).to.equal(expectedTo); - expect(logArgs._value).to.be.bignumber.equal(ethTokensToWithdraw); + expect(logArgs._value).to.be.bignumber.equal(expectedValue); }); }); diff --git a/packages/contracts/test/ts/exchange/core.ts b/packages/contracts/test/ts/exchange/core.ts index 641084937b..aef2b5a5db 100644 --- a/packages/contracts/test/ts/exchange/core.ts +++ b/packages/contracts/test/ts/exchange/core.ts @@ -748,7 +748,6 @@ contract('Exchange', (accounts: string[]) => { const res = await exWrapper.cancelOrderAsync(order, maker); expect(res.logs).to.have.length(1); - const errId = res.logs[0].args.errorId.toNumber(); const errCode = res.logs[0].args.errorId.toNumber(); expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED_OR_CANCELLED); }); diff --git a/packages/contracts/test/ts/exchange/wrapper.ts b/packages/contracts/test/ts/exchange/wrapper.ts index 3097f68876..13a0b60582 100644 --- a/packages/contracts/test/ts/exchange/wrapper.ts +++ b/packages/contracts/test/ts/exchange/wrapper.ts @@ -227,8 +227,6 @@ contract('Exchange', (accounts: string[]) => { it('should throw if a single order does not fill the expected amount', async () => { const fillTakerTokenAmounts: BigNumber[] = []; - const makerToken = rep.address; - const takerToken = dgd.address; orders.forEach(order => { const fillTakerTokenAmount = order.params.takerTokenAmount.div(2); fillTakerTokenAmounts.push(fillTakerTokenAmount); @@ -311,7 +309,7 @@ contract('Exchange', (accounts: string[]) => { const cancelTakerTokenAmounts = _.map(orders, order => order.params.takerTokenAmount); await exWrapper.batchCancelOrdersAsync(orders, maker, {cancelTakerTokenAmounts}); - const res = await exWrapper.batchFillOrdersAsync( + await exWrapper.batchFillOrdersAsync( orders, taker, {fillTakerTokenAmounts: cancelTakerTokenAmounts}); const newBalances = await dmyBalances.getAsync(); expect(balances).to.be.deep.equal(newBalances); diff --git a/packages/contracts/util/crypto.ts b/packages/contracts/util/crypto.ts index 5253b8c159..2e43ae816e 100644 --- a/packages/contracts/util/crypto.ts +++ b/packages/contracts/util/crypto.ts @@ -1,4 +1,3 @@ -import {BigNumber} from 'bignumber.js'; import BN = require('bn.js'); import ABI = require('ethereumjs-abi'); import ethUtil = require('ethereumjs-util'); diff --git a/packages/dev-utils/src/rpc.ts b/packages/dev-utils/src/rpc.ts index c276b24523..19834dbb4e 100644 --- a/packages/dev-utils/src/rpc.ts +++ b/packages/dev-utils/src/rpc.ts @@ -3,7 +3,6 @@ import * as request from 'request-promise-native'; export class RPC { private url: string; - private port: number; private id: number; constructor(url: string) { this.url = url; diff --git a/packages/json-schemas/test/schema_test.ts b/packages/json-schemas/test/schema_test.ts index 8a2f9407d7..067b02827e 100644 --- a/packages/json-schemas/test/schema_test.ts +++ b/packages/json-schemas/test/schema_test.ts @@ -1,4 +1,3 @@ -import {promisify} from '@0xproject/utils'; import BigNumber from 'bignumber.js'; import * as chai from 'chai'; import * as dirtyChai from 'dirty-chai'; @@ -16,7 +15,6 @@ const { addressSchema, ecSignatureSchema, ecSignatureParameterSchema, - indexFilterValuesSchema, orderCancellationRequestsSchema, orderFillOrKillRequestsSchema, orderFillRequestsSchema, diff --git a/packages/kovan-faucets/src/ts/error_reporter.ts b/packages/kovan-faucets/src/ts/error_reporter.ts index db21647c04..bc1844aa2b 100644 --- a/packages/kovan-faucets/src/ts/error_reporter.ts +++ b/packages/kovan-faucets/src/ts/error_reporter.ts @@ -1,5 +1,4 @@ import * as express from 'express'; -import * as fs from 'fs'; import rollbar = require('rollbar'); import {configs} from './configs'; diff --git a/packages/kovan-faucets/src/ts/utils.ts b/packages/kovan-faucets/src/ts/utils.ts index 6a0b262e60..893f82ca38 100644 --- a/packages/kovan-faucets/src/ts/utils.ts +++ b/packages/kovan-faucets/src/ts/utils.ts @@ -1,6 +1,3 @@ -import * as express from 'express'; -import * as _ from 'lodash'; - export const utils = { consoleLog(message: string) { /* tslint:disable */ diff --git a/packages/kovan-faucets/src/ts/zrx_request_queue.ts b/packages/kovan-faucets/src/ts/zrx_request_queue.ts index 6e6590e7c5..3b505690f3 100644 --- a/packages/kovan-faucets/src/ts/zrx_request_queue.ts +++ b/packages/kovan-faucets/src/ts/zrx_request_queue.ts @@ -1,5 +1,4 @@ import {ZeroEx} from '0x.js'; -import {promisify} from '@0xproject/utils'; import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts index 578f6ff6f8..9ffc105a45 100644 --- a/packages/subproviders/src/subproviders/ledger.ts +++ b/packages/subproviders/src/subproviders/ledger.ts @@ -1,9 +1,7 @@ import {assert} from '@0xproject/assert'; import {addressUtils} from '@0xproject/utils'; -import promisify = require('es6-promisify'); import EthereumTx = require('ethereumjs-tx'); import ethUtil = require('ethereumjs-util'); -import * as ledger from 'ledgerco'; import * as _ from 'lodash'; import Semaphore from 'semaphore-async-await'; import Web3 = require('web3'); @@ -23,7 +21,6 @@ const DEFAULT_DERIVATION_PATH = `44'/60'/0'`; const NUM_ADDRESSES_TO_FETCH = 10; const ASK_FOR_ON_DEVICE_CONFIRMATION = false; const SHOULD_GET_CHAIN_CODE = false; -const HEX_REGEX = /^[0-9A-Fa-f]+$/g; export class LedgerSubprovider extends Subprovider { private _nonceLock: Semaphore; @@ -34,18 +31,6 @@ export class LedgerSubprovider extends Subprovider { private _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync; private _ledgerClientIfExists?: LedgerEthereumClient; private _shouldAlwaysAskForConfirmation: boolean; - private static isValidHex(data: string) { - if (!_.isString(data)) { - return false; - } - const isHexPrefixed = data.slice(0, 2) === '0x'; - if (!isHexPrefixed) { - return false; - } - const nonPrefixed = data.slice(2); - const isValid = nonPrefixed.match(HEX_REGEX); - return isValid; - } private static validateSender(sender: string) { if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) { throw new Error(LedgerSubproviderErrors.SenderInvalidOrNotSupplied); diff --git a/packages/subproviders/src/subproviders/subprovider.ts b/packages/subproviders/src/subproviders/subprovider.ts index 64d97b9589..41dde42384 100644 --- a/packages/subproviders/src/subproviders/subprovider.ts +++ b/packages/subproviders/src/subproviders/subprovider.ts @@ -11,7 +11,6 @@ import { */ export class Subprovider { private engine: any; - private currentBlock: any; // Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js private static getRandomId() { const extraDigits = 3; @@ -34,9 +33,6 @@ export class Subprovider { } public setEngine(engine: any): void { this.engine = engine; - engine.on('block', (block: any) => { - this.currentBlock = block; - }); } public async emitPayloadAsync(payload: JSONRPCPayload): Promise { const finalPayload = Subprovider.createFinalPayload(payload); diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts index 38dc1e67e4..1e7d3eab09 100644 --- a/packages/subproviders/src/types.ts +++ b/packages/subproviders/src/types.ts @@ -1,5 +1,4 @@ import * as _ from 'lodash'; -import * as Web3 from 'web3'; export interface LedgerCommunicationClient { close_async: () => Promise; diff --git a/packages/subproviders/test/integration/ledger_subprovider_test.ts b/packages/subproviders/test/integration/ledger_subprovider_test.ts index 75f6d47fe5..ab4ffb19ab 100644 --- a/packages/subproviders/test/integration/ledger_subprovider_test.ts +++ b/packages/subproviders/test/integration/ledger_subprovider_test.ts @@ -2,20 +2,16 @@ import * as chai from 'chai'; import promisify = require('es6-promisify'); import * as ethUtils from 'ethereumjs-util'; import * as _ from 'lodash'; -import * as mocha from 'mocha'; import Web3 = require('web3'); import Web3ProviderEngine = require('web3-provider-engine'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import { - ECSignature, ledgerEthereumNodeJsClientFactoryAsync, LedgerSubprovider, } from '../../src'; import { DoneCallback, - LedgerGetAddressResult, - PartialTxParams, } from '../../src/types'; import {chaiSetup} from '../chai_setup'; import {reportCallbackErrors} from '../utils/report_callback_errors'; diff --git a/packages/subproviders/test/unit/ledger_subprovider_test.ts b/packages/subproviders/test/unit/ledger_subprovider_test.ts index 964df5db9a..bd4d933253 100644 --- a/packages/subproviders/test/unit/ledger_subprovider_test.ts +++ b/packages/subproviders/test/unit/ledger_subprovider_test.ts @@ -6,14 +6,11 @@ import Web3ProviderEngine = require('web3-provider-engine'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import { - ECSignature, LedgerSubprovider, } from '../../src'; import { DoneCallback, - ECSignatureString, LedgerCommunicationClient, - LedgerGetAddressResult, LedgerSubproviderErrors, } from '../../src/types'; import {chaiSetup} from '../chai_setup'; diff --git a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts index edeb1d5a20..2f1676915e 100644 --- a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts +++ b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts @@ -11,6 +11,7 @@ import {chaiSetup} from '../chai_setup'; import {reportCallbackErrors} from '../utils/report_callback_errors'; const expect = chai.expect; +chaiSetup.configure(); describe('RedundantRpcSubprovider', () => { let provider: Web3ProviderEngine; diff --git a/packages/tslint-config/rules/walkers/async_suffix.ts b/packages/tslint-config/rules/walkers/async_suffix.ts index 0c89bd99f6..7fa7a78b80 100644 --- a/packages/tslint-config/rules/walkers/async_suffix.ts +++ b/packages/tslint-config/rules/walkers/async_suffix.ts @@ -9,6 +9,7 @@ export class AsyncSuffixWalker extends Lint.RuleWalker { const methodName = methodNameNode.getText(); if (!_.isUndefined(node.type)) { if (node.type.kind === ts.SyntaxKind.TypeReference) { + // tslint:disable-next-line:no-unnecessary-type-assertion const returnTypeName = (node.type as ts.TypeReferenceNode).typeName.getText(); if (returnTypeName === 'Promise' && !methodName.endsWith('Async')) { const failure = this.createFailure( From 2041e9945edf6f5255185de73c0dc332270e4bbb Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 15 Dec 2017 15:19:19 +0100 Subject: [PATCH 013/125] Fix website unused vars --- packages/website/ts/blockchain.ts | 12 +------ .../dialogs/portal_disclaimer_dialog.tsx | 1 - .../ts/components/dialogs/send_dialog.tsx | 5 +-- .../track_token_confirmation_dialog.tsx | 2 -- packages/website/ts/components/fill_order.tsx | 10 ------ packages/website/ts/components/footer.tsx | 33 +------------------ .../generate_order/asset_picker.tsx | 21 ------------ .../generate_order/generate_order_form.tsx | 13 ++------ .../generate_order/new_token_form.tsx | 1 - .../ts/components/inputs/address_input.tsx | 1 - .../inputs/identicon_address_input.tsx | 2 -- .../ts/components/inputs/token_input.tsx | 2 +- packages/website/ts/components/order_json.tsx | 4 +-- packages/website/ts/components/portal.tsx | 27 --------------- .../website/ts/components/portal_menu.tsx | 1 - .../website/ts/components/send_button.tsx | 2 -- .../website/ts/components/token_balances.tsx | 1 - packages/website/ts/components/top_bar.tsx | 10 +----- .../ts/components/top_bar_menu_item.tsx | 1 - .../components/track_token_confirmation.tsx | 2 -- packages/website/ts/components/ui/badge.tsx | 1 - .../ts/components/ui/drop_down_menu_item.tsx | 6 ---- .../ts/components/ui/fake_text_field.tsx | 1 - .../components/ui/lifecycle_raised_button.tsx | 2 -- .../website/ts/components/ui/menu_item.tsx | 3 -- packages/website/ts/components/ui/party.tsx | 4 --- .../ts/components/ui/simple_loading.tsx | 1 - .../website/ts/components/ui/swap_icon.tsx | 1 - .../website/ts/components/visual_order.tsx | 1 - .../ts/containers/connect_documentation.tsx | 4 +-- .../ts/containers/generate_order_form.tsx | 2 -- packages/website/ts/containers/portal.tsx | 12 +++---- .../smart_contracts_documentation.tsx | 2 +- .../containers/zero_ex_js_documentation.tsx | 4 +-- packages/website/ts/index.tsx | 2 +- packages/website/ts/pages/about/about.tsx | 6 +--- packages/website/ts/pages/about/profile.tsx | 3 -- .../ts/pages/documentation/comment.tsx | 2 ++ .../ts/pages/documentation/documentation.tsx | 8 ----- .../website/ts/pages/documentation/enum.tsx | 6 +--- .../pages/documentation/event_definition.tsx | 3 -- .../ts/pages/documentation/method_block.tsx | 4 --- .../pages/documentation/method_signature.tsx | 2 ++ .../ts/pages/documentation/source_link.tsx | 1 - .../website/ts/pages/documentation/type.tsx | 3 -- .../pages/documentation/type_definition.tsx | 2 -- packages/website/ts/pages/faq/faq.tsx | 2 -- packages/website/ts/pages/landing/landing.tsx | 26 ++------------- packages/website/ts/pages/not_found.tsx | 1 - .../ts/pages/shared/nested_sidebar_menu.tsx | 3 +- .../ts/pages/shared/version_drop_down.tsx | 2 -- packages/website/ts/redux/dispatcher.ts | 1 - packages/website/ts/utils/constants.ts | 1 - packages/website/ts/utils/doc_utils.ts | 1 - packages/website/ts/utils/typedoc_utils.ts | 4 --- packages/website/ts/utils/utils.ts | 6 ++-- 56 files changed, 28 insertions(+), 256 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 321a603efc..331bafa717 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -1,7 +1,6 @@ import { BlockParam, DecodedLogEvent, - ExchangeContractErrs, ExchangeContractEventArgs, ExchangeEvents, IndexedFilterValues, @@ -14,7 +13,6 @@ import { Token as ZeroExToken, TransactionReceiptWithDecodedLogs, ZeroEx, - ZeroExError, } from '0x.js'; import { InjectedWeb3Subprovider, @@ -25,9 +23,6 @@ import { } from '@0xproject/subproviders'; import {intervalUtils, promisify} from '@0xproject/utils'; import BigNumber from 'bignumber.js'; -import compareVersions = require('compare-versions'); -import ethUtil = require('ethereumjs-util'); -import findVersions = require('find-versions'); import * as _ from 'lodash'; import * as React from 'react'; import contract = require('truffle-contract'); @@ -40,7 +35,6 @@ import { BlockchainCallErrs, BlockchainErrs, ContractInstance, - ContractResponse, EtherscanLinkSuffixes, ProviderType, Side, @@ -60,7 +54,6 @@ import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); import * as MintableArtifacts from '../contracts/Mintable.json'; -const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 45730; const BLOCK_NUMBER_BACK_TRACK = 50; export class Blockchain { @@ -70,8 +63,6 @@ export class Blockchain { private dispatcher: Dispatcher; private web3Wrapper?: Web3Wrapper; private exchangeAddress: string; - private tokenTransferProxy: ContractInstance; - private tokenRegistry: ContractInstance; private userAddress: string; private cachedProvider: Web3.Provider; private ledgerSubprovider: LedgerWalletSubprovider; @@ -506,8 +497,7 @@ export class Blockchain { await this.fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues); // Start a subscription for new logs - const exchangeAddress = this.getExchangeContractAddressIfExists(); - const subscriptionId = this.zeroEx.exchange.subscribe( + this.zeroEx.exchange.subscribe( ExchangeEvents.LogFill, indexFilterValues, async (err: Error, decodedLogEvent: DecodedLogEvent) => { if (err) { diff --git a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx index 1d90624ee6..bfff7fbb57 100644 --- a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx +++ b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx @@ -2,7 +2,6 @@ import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; import {colors} from 'material-ui/styles'; import * as React from 'react'; -import {constants} from 'ts/utils/constants'; interface PortalDisclaimerDialogProps { isOpen: boolean; diff --git a/packages/website/ts/components/dialogs/send_dialog.tsx b/packages/website/ts/components/dialogs/send_dialog.tsx index 31afc3386e..0f35169938 100644 --- a/packages/website/ts/components/dialogs/send_dialog.tsx +++ b/packages/website/ts/components/dialogs/send_dialog.tsx @@ -2,13 +2,10 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; -import RadioButton from 'material-ui/RadioButton'; -import RadioButtonGroup from 'material-ui/RadioButton/RadioButtonGroup'; import * as React from 'react'; import {AddressInput} from 'ts/components/inputs/address_input'; -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 {Token, TokenState} from 'ts/types'; interface SendDialogProps { onComplete: (recipient: string, value: BigNumber) => void; 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 70c7d1ab6c..2d45009d50 100644 --- a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx +++ b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx @@ -1,14 +1,12 @@ import * as _ from 'lodash'; import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; -import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Blockchain} from 'ts/blockchain'; import {TrackTokenConfirmation} from 'ts/components/track_token_confirmation'; import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; import {Dispatcher} from 'ts/redux/dispatcher'; import {Token, TokenByAddress} from 'ts/types'; -import {constants} from 'ts/utils/constants'; interface TrackTokenConfirmationDialogProps { tokens: Token[]; diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 388c72d8e5..40a9b87d67 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -4,10 +4,7 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import {Card, CardHeader, CardText} from 'material-ui/Card'; import Divider from 'material-ui/Divider'; -import Paper from 'material-ui/Paper'; import RaisedButton from 'material-ui/RaisedButton'; -import TextField from 'material-ui/TextField'; -import * as moment from 'moment'; import * as React from 'react'; import {Link} from 'react-router-dom'; import {Blockchain} from 'ts/blockchain'; @@ -19,18 +16,13 @@ import {Alert} from 'ts/components/ui/alert'; import {EthereumAddress} from 'ts/components/ui/ethereum_address'; import {Identicon} from 'ts/components/ui/identicon'; import {VisualOrder} from 'ts/components/visual_order'; -import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; import {Dispatcher} from 'ts/redux/dispatcher'; import {orderSchema} from 'ts/schemas/order_schema'; import {SchemaValidator} from 'ts/schemas/validator'; import { AlertTypes, BlockchainErrs, - ContractResponse, - ExchangeContractErrs, Order, - OrderToken, - Side, Token, TokenByAddress, TokenStateByAddress, @@ -531,8 +523,6 @@ export class FillOrder extends React.Component { }); const parsedOrder = this.state.parsedOrder; - const orderHash = parsedOrder.signature.hash; - const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); const takerFillAmount = this.props.orderFillAmount; if (_.isUndefined(this.props.userAddress)) { diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx index 5e3c0479ad..b265230945 100644 --- a/packages/website/ts/components/footer.tsx +++ b/packages/website/ts/components/footer.tsx @@ -3,11 +3,7 @@ import * as React from 'react'; import { Link, } from 'react-router-dom'; -import {HashLink} from 'react-router-hash-link'; -import { - Link as ScrollLink, -} from 'react-scroll'; -import {Styles, WebsitePaths} from 'ts/types'; +import {WebsitePaths} from 'ts/types'; import {constants} from 'ts/utils/constants'; interface MenuItemsBySection { @@ -229,31 +225,4 @@ export class Footer extends React.Component { ); } - private renderHomepageLink(title: string) { - const hash = title.toLowerCase(); - if (this.props.location.pathname === WebsitePaths.Home) { - return ( - - {title} - - ); - } else { - return ( - - {title} - - ); - } - } } diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx index 633d6a017b..08b4a909ee 100644 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ b/packages/website/ts/components/generate_order/asset_picker.tsx @@ -1,9 +1,6 @@ import * as _ from 'lodash'; import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; -import GridList from 'material-ui/GridList/GridList'; -import GridTile from 'material-ui/GridList/GridTile'; -import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Blockchain} from 'ts/blockchain'; import {NewTokenForm} from 'ts/components/generate_order/new_token_form'; @@ -12,15 +9,12 @@ import {TokenIcon} from 'ts/components/ui/token_icon'; import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; import {Dispatcher} from 'ts/redux/dispatcher'; import { - AssetToken, DialogConfigs, - Styles, Token, TokenByAddress, TokenState, TokenVisibility, } from 'ts/types'; -import {utils} from 'ts/utils/utils'; const TOKEN_ICON_DIMENSION = 100; const TILE_DIMENSION = 146; @@ -219,21 +213,6 @@ export class AssetPicker extends React.Component { +export class GenerateOrderForm extends React.Component { private validator: SchemaValidator; constructor(props: GenerateOrderFormProps) { super(props); @@ -287,7 +278,7 @@ export class GenerateOrderForm extends React.Component { private blockchain: Blockchain; private sharedOrderIfExists: Order; diff --git a/packages/website/ts/components/portal_menu.tsx b/packages/website/ts/components/portal_menu.tsx index 869df3e716..7b9a31a150 100644 --- a/packages/website/ts/components/portal_menu.tsx +++ b/packages/website/ts/components/portal_menu.tsx @@ -1,6 +1,5 @@ import * as _ from 'lodash'; import * as React from 'react'; -import {Link} from 'react-router-dom'; import {MenuItem} from 'ts/components/ui/menu_item'; import {WebsitePaths} from 'ts/types'; diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx index da8dd2a9b2..b3fd2aebae 100644 --- a/packages/website/ts/components/send_button.tsx +++ b/packages/website/ts/components/send_button.tsx @@ -1,4 +1,3 @@ -import {ZeroEx} from '0x.js'; import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import RaisedButton from 'material-ui/RaisedButton'; @@ -7,7 +6,6 @@ import {Blockchain} from 'ts/blockchain'; import {SendDialog} from 'ts/components/dialogs/send_dialog'; import {Dispatcher} from 'ts/redux/dispatcher'; import {BlockchainCallErrs, Token, TokenState} from 'ts/types'; -import {constants} from 'ts/utils/constants'; import {errorReporter} from 'ts/utils/error_reporter'; import {utils} from 'ts/utils/utils'; diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index ae5ef9222f..4781145167 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -18,7 +18,6 @@ import { TableRow, TableRowColumn, } from 'material-ui/Table'; -import QueryString = require('query-string'); import * as React from 'react'; import ReactTooltip = require('react-tooltip'); import firstBy = require('thenby'); diff --git a/packages/website/ts/components/top_bar.tsx b/packages/website/ts/components/top_bar.tsx index 4398fe6672..488eef9141 100644 --- a/packages/website/ts/components/top_bar.tsx +++ b/packages/website/ts/components/top_bar.tsx @@ -1,15 +1,9 @@ import * as _ from 'lodash'; -import AppBar from 'material-ui/AppBar'; import Drawer from 'material-ui/Drawer'; import MenuItem from 'material-ui/MenuItem'; import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Link} from 'react-router-dom'; -import {HashLink} from 'react-router-hash-link'; -import { - animateScroll, - Link as ScrollLink, -} from 'react-scroll'; import ReactTooltip = require('react-tooltip'); import {PortalMenu} from 'ts/components/portal_menu'; import {TopBarMenuItem} from 'ts/components/top_bar_menu_item'; @@ -17,10 +11,8 @@ import {DropDownMenuItem} from 'ts/components/ui/drop_down_menu_item'; 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 {DocsMenu, MenuSubsectionsBySection, Styles, TypeDocNode, WebsitePaths} from 'ts/types'; -import {configs} from 'ts/utils/configs'; +import {DocsMenu, MenuSubsectionsBySection, Styles, WebsitePaths} from 'ts/types'; import {constants} from 'ts/utils/constants'; -import {typeDocUtils} from 'ts/utils/typedoc_utils'; const CUSTOM_DARK_GRAY = '#231F20'; const SECTION_HEADER_COLOR = 'rgb(234, 234, 234)'; diff --git a/packages/website/ts/components/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar_menu_item.tsx index de429fba6f..38dc911940 100644 --- a/packages/website/ts/components/top_bar_menu_item.tsx +++ b/packages/website/ts/components/top_bar_menu_item.tsx @@ -1,7 +1,6 @@ import * as _ from 'lodash'; import * as React from 'react'; import {Link} from 'react-router-dom'; -import {Styles} from 'ts/types'; const CUSTOM_DARK_GRAY = '#231F20'; const DEFAULT_STYLE = { diff --git a/packages/website/ts/components/track_token_confirmation.tsx b/packages/website/ts/components/track_token_confirmation.tsx index b9b2ef18a3..cd588f239d 100644 --- a/packages/website/ts/components/track_token_confirmation.tsx +++ b/packages/website/ts/components/track_token_confirmation.tsx @@ -1,6 +1,4 @@ import * as _ from 'lodash'; -import Dialog from 'material-ui/Dialog'; -import FlatButton from 'material-ui/FlatButton'; import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Party} from 'ts/components/ui/party'; diff --git a/packages/website/ts/components/ui/badge.tsx b/packages/website/ts/components/ui/badge.tsx index 15d5ea2278..cf43960470 100644 --- a/packages/website/ts/components/ui/badge.tsx +++ b/packages/website/ts/components/ui/badge.tsx @@ -1,5 +1,4 @@ import * as _ from 'lodash'; -import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Styles} from 'ts/types'; diff --git a/packages/website/ts/components/ui/drop_down_menu_item.tsx b/packages/website/ts/components/ui/drop_down_menu_item.tsx index 05b88f7ce0..041688a9de 100644 --- a/packages/website/ts/components/ui/drop_down_menu_item.tsx +++ b/packages/website/ts/components/ui/drop_down_menu_item.tsx @@ -1,13 +1,7 @@ import * as _ from 'lodash'; import Menu from 'material-ui/Menu'; -import MenuItem from 'material-ui/MenuItem'; import Popover from 'material-ui/Popover'; import * as React from 'react'; -import {Link} from 'react-router-dom'; -import { - Link as ScrollLink, -} from 'react-scroll'; -import {Styles, WebsitePaths} from 'ts/types'; const CHECK_CLOSE_POPOVER_INTERVAL_MS = 300; const CUSTOM_LIGHT_GRAY = '#848484'; diff --git a/packages/website/ts/components/ui/fake_text_field.tsx b/packages/website/ts/components/ui/fake_text_field.tsx index 90bc47f018..8ee4349a2f 100644 --- a/packages/website/ts/components/ui/fake_text_field.tsx +++ b/packages/website/ts/components/ui/fake_text_field.tsx @@ -1,4 +1,3 @@ -import {colors} from 'material-ui/styles'; import * as React from 'react'; import {InputLabel} from 'ts/components/ui/input_label'; import {Styles} from 'ts/types'; diff --git a/packages/website/ts/components/ui/lifecycle_raised_button.tsx b/packages/website/ts/components/ui/lifecycle_raised_button.tsx index cba94ca8ce..2d668fb827 100644 --- a/packages/website/ts/components/ui/lifecycle_raised_button.tsx +++ b/packages/website/ts/components/ui/lifecycle_raised_button.tsx @@ -1,8 +1,6 @@ import * as _ from 'lodash'; import RaisedButton from 'material-ui/RaisedButton'; import * as React from 'react'; -import {Blockchain} from 'ts/blockchain'; -import {Token} from 'ts/types'; import {utils} from 'ts/utils/utils'; const COMPLETE_STATE_SHOW_LENGTH_MS = 2000; diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/menu_item.tsx index 862f284576..c7ce7439f9 100644 --- a/packages/website/ts/components/ui/menu_item.tsx +++ b/packages/website/ts/components/ui/menu_item.tsx @@ -1,9 +1,6 @@ import * as _ from 'lodash'; -import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Link} from 'react-router-dom'; -import {Styles} from 'ts/types'; -import {constants} from 'ts/utils/constants'; interface MenuItemProps { to: string; diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx index 5bafa6071b..e6b6ea9e26 100644 --- a/packages/website/ts/components/ui/party.tsx +++ b/packages/website/ts/components/ui/party.tsx @@ -7,7 +7,6 @@ import {Identicon} from 'ts/components/ui/identicon'; import {EtherscanLinkSuffixes} from 'ts/types'; import {utils} from 'ts/utils/utils'; -const MIN_ADDRESS_WIDTH = 60; const IMAGE_DIMENSION = 100; const IDENTICON_DIAMETER = 95; const CHECK_MARK_GREEN = 'rgb(0, 195, 62)'; @@ -33,10 +32,7 @@ export class Party extends React.Component { public render() { const label = this.props.label; const address = this.props.address; - const tooltipId = `${label}-${address}-tooltip`; const identiconDiameter = this.props.identiconDiameter; - const addressWidth = identiconDiameter > MIN_ADDRESS_WIDTH ? - identiconDiameter : MIN_ADDRESS_WIDTH; const emptyIdenticonStyles = { width: identiconDiameter, height: identiconDiameter, diff --git a/packages/website/ts/components/ui/simple_loading.tsx b/packages/website/ts/components/ui/simple_loading.tsx index d55d7851d0..fa548f996a 100644 --- a/packages/website/ts/components/ui/simple_loading.tsx +++ b/packages/website/ts/components/ui/simple_loading.tsx @@ -1,5 +1,4 @@ import CircularProgress from 'material-ui/CircularProgress'; -import {colors} from 'material-ui/styles'; import * as React from 'react'; export interface SimpleLoadingProps { diff --git a/packages/website/ts/components/ui/swap_icon.tsx b/packages/website/ts/components/ui/swap_icon.tsx index 2e6ae89bb0..b5d4b0caad 100644 --- a/packages/website/ts/components/ui/swap_icon.tsx +++ b/packages/website/ts/components/ui/swap_icon.tsx @@ -1,7 +1,6 @@ import * as _ from 'lodash'; import {colors} from 'material-ui/styles'; import * as React from 'react'; -import {constants} from 'ts/utils/constants'; interface SwapIconProps { swapTokensFn: () => void; diff --git a/packages/website/ts/components/visual_order.tsx b/packages/website/ts/components/visual_order.tsx index 037a321ffa..a7be6f7808 100644 --- a/packages/website/ts/components/visual_order.tsx +++ b/packages/website/ts/components/visual_order.tsx @@ -3,7 +3,6 @@ import * as _ from 'lodash'; import * as React from 'react'; import {Party} from 'ts/components/ui/party'; import {AssetToken, Token, TokenByAddress} from 'ts/types'; -import {constants} from 'ts/utils/constants'; import {utils} from 'ts/utils/utils'; const PRECISION = 5; diff --git a/packages/website/ts/containers/connect_documentation.tsx b/packages/website/ts/containers/connect_documentation.tsx index 8ecc1e37c5..3b181a814e 100644 --- a/packages/website/ts/containers/connect_documentation.tsx +++ b/packages/website/ts/containers/connect_documentation.tsx @@ -1,9 +1,7 @@ -import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import * as React from 'react'; import {connect} from 'react-redux'; -import {Dispatch, Store as ReduxStore} from 'redux'; -import {Blockchain} from 'ts/blockchain'; +import {Dispatch} from 'redux'; import {DocsInfo} from 'ts/pages/documentation/docs_info'; import { Documentation as DocumentationComponent, diff --git a/packages/website/ts/containers/generate_order_form.tsx b/packages/website/ts/containers/generate_order_form.tsx index 864d2702e8..134c84859c 100644 --- a/packages/website/ts/containers/generate_order_form.tsx +++ b/packages/website/ts/containers/generate_order_form.tsx @@ -1,8 +1,6 @@ -import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import * as React from 'react'; import {connect} from 'react-redux'; -import {Dispatch, Store as ReduxStore} from 'redux'; import {Blockchain} from 'ts/blockchain'; import {GenerateOrderForm as GenerateOrderFormComponent} from 'ts/components/generate_order/generate_order_form'; import {Dispatcher} from 'ts/redux/dispatcher'; diff --git a/packages/website/ts/containers/portal.tsx b/packages/website/ts/containers/portal.tsx index 2987764f4c..7588a1535d 100644 --- a/packages/website/ts/containers/portal.tsx +++ b/packages/website/ts/containers/portal.tsx @@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import * as React from 'react'; import {connect} from 'react-redux'; -import {Dispatch, Store as ReduxStore} from 'redux'; +import {Dispatch} from 'redux'; import { Portal as PortalComponent, PortalAllProps as PortalComponentAllProps, @@ -12,7 +12,6 @@ import {Dispatcher} from 'ts/redux/dispatcher'; import {State} from 'ts/redux/reducer'; import { BlockchainErrs, - Fill, HashData, Order, ScreenWidths, @@ -22,24 +21,23 @@ import { } from 'ts/types'; import {constants} from 'ts/utils/constants'; -interface MapStateToProps { +interface ConnectedState { blockchainErr: BlockchainErrs; blockchainIsLoaded: boolean; hashData: HashData; networkId: number; nodeVersion: string; - orderFillAmount: number; + orderFillAmount: BigNumber; tokenByAddress: TokenByAddress; tokenStateByAddress: TokenStateByAddress; - userEtherBalance: number; + userEtherBalance: BigNumber; screenWidth: ScreenWidths; shouldBlockchainErrDialogBeOpen: boolean; userAddress: string; userSuppliedOrderCache: Order; + flashMessage?: string|React.ReactNode; } -interface ConnectedState {} - interface ConnectedDispatch { dispatcher: Dispatcher; } diff --git a/packages/website/ts/containers/smart_contracts_documentation.tsx b/packages/website/ts/containers/smart_contracts_documentation.tsx index ea2b19b8c9..1f82bda0f3 100644 --- a/packages/website/ts/containers/smart_contracts_documentation.tsx +++ b/packages/website/ts/containers/smart_contracts_documentation.tsx @@ -1,7 +1,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import {connect} from 'react-redux'; -import {Dispatch, Store as ReduxStore} from 'redux'; +import {Dispatch} from 'redux'; import {DocsInfo} from 'ts/pages/documentation/docs_info'; import { Documentation as DocumentationComponent, diff --git a/packages/website/ts/containers/zero_ex_js_documentation.tsx b/packages/website/ts/containers/zero_ex_js_documentation.tsx index 58c0ee186d..1ce9ba139d 100644 --- a/packages/website/ts/containers/zero_ex_js_documentation.tsx +++ b/packages/website/ts/containers/zero_ex_js_documentation.tsx @@ -1,9 +1,7 @@ -import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import * as React from 'react'; import {connect} from 'react-redux'; -import {Dispatch, Store as ReduxStore} from 'redux'; -import {Blockchain} from 'ts/blockchain'; +import {Dispatch} from 'redux'; import {DocsInfo} from 'ts/pages/documentation/docs_info'; import { Documentation as DocumentationComponent, diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 922102d969..cbefac8bd8 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -6,7 +6,7 @@ import {colors, getMuiTheme, MuiThemeProvider} from 'material-ui/styles'; import * as React from 'react'; import {render} from 'react-dom'; import {Provider} from 'react-redux'; -import {BrowserRouter as Router, Link, Redirect, Route, Switch} from 'react-router-dom'; +import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom'; import * as injectTapEventPlugin from 'react-tap-event-plugin'; import {createStore, Store as ReduxStore} from 'redux'; import {createLazyComponent} from 'ts/lazy_component'; diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 3af05e8a40..011bbb9d9a 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -1,15 +1,11 @@ import * as _ from 'lodash'; -import RaisedButton from 'material-ui/RaisedButton'; import {colors} from 'material-ui/styles'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; -import {Link} from 'react-router-dom'; import {Footer} from 'ts/components/footer'; import {TopBar} from 'ts/components/top_bar'; import {Profile} from 'ts/pages/about/profile'; -import {Question} from 'ts/pages/faq/question'; import {ProfileInfo, Styles} from 'ts/types'; -import {configs} from 'ts/utils/configs'; import {constants} from 'ts/utils/constants'; import {utils} from 'ts/utils/utils'; @@ -236,7 +232,7 @@ export class About extends React.Component { } private renderProfiles(profiles: ProfileInfo[]) { const numIndiv = profiles.length; - const colSize = utils.getColSize(profiles.length); + const colSize = utils.getColSize(numIndiv); return _.map(profiles, profile => { return (
= (props: CommentProps) => {
); }; + +Comment.defaultProps = defaultProps; diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx index be99e77a2c..0ca8ae9d3a 100644 --- a/packages/website/ts/pages/documentation/documentation.tsx +++ b/packages/website/ts/pages/documentation/documentation.tsx @@ -17,35 +17,27 @@ import {MethodBlock} from 'ts/pages/documentation/method_block'; import {SourceLink} from 'ts/pages/documentation/source_link'; import {Type} from 'ts/pages/documentation/type'; import {TypeDefinition} from 'ts/pages/documentation/type_definition'; -import {AnchorTitle} from 'ts/pages/shared/anchor_title'; 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'; import {Dispatcher} from 'ts/redux/dispatcher'; import { AddressByContractName, - CustomType, DocAgnosticFormat, - Docs, - DocsInfoConfig, DoxityDocObj, EtherscanLinkSuffixes, Event, - MenuSubsectionsBySection, Networks, Property, SolidityMethod, Styles, TypeDefinitionByName, - TypeDocNode, TypescriptMethod, - WebsitePaths, } from 'ts/types'; import {constants} from 'ts/utils/constants'; import {docUtils} from 'ts/utils/doc_utils'; import {utils} from 'ts/utils/utils'; -const SCROLL_TO_TIMEOUT = 500; const SCROLL_TOP_ID = 'docsScrollTop'; const CUSTOM_PURPLE = '#690596'; const CUSTOM_RED = '#e91751'; diff --git a/packages/website/ts/pages/documentation/enum.tsx b/packages/website/ts/pages/documentation/enum.tsx index 8fcd2c252a..b5fbc4bd25 100644 --- a/packages/website/ts/pages/documentation/enum.tsx +++ b/packages/website/ts/pages/documentation/enum.tsx @@ -1,9 +1,6 @@ import * as _ from 'lodash'; import * as React from 'react'; -import {EnumValue, TypeDocNode} from 'ts/types'; -import {utils} from 'ts/utils/utils'; - -const STRING_ENUM_CODE_PREFIX = ' strEnum('; +import {EnumValue} from 'ts/types'; interface EnumProps { values: EnumValue[]; @@ -11,7 +8,6 @@ interface EnumProps { export function Enum(props: EnumProps) { const values = _.map(props.values, (value, i) => { - const isLast = i === props.values.length - 1; const defaultValueIfAny = !_.isUndefined(value.defaultValue) ? ` = ${value.defaultValue}` : ''; return `\n\t${value.name}${defaultValueIfAny},`; }); diff --git a/packages/website/ts/pages/documentation/event_definition.tsx b/packages/website/ts/pages/documentation/event_definition.tsx index 469e6bb37d..3c57666e70 100644 --- a/packages/website/ts/pages/documentation/event_definition.tsx +++ b/packages/website/ts/pages/documentation/event_definition.tsx @@ -4,10 +4,7 @@ import {DocsInfo} from 'ts/pages/documentation/docs_info'; import {Type} from 'ts/pages/documentation/type'; import {AnchorTitle} from 'ts/pages/shared/anchor_title'; import {Event, EventArg, HeaderSizes} from 'ts/types'; -import {constants} from 'ts/utils/constants'; -import {utils} from 'ts/utils/utils'; -const KEYWORD_COLOR = '#a81ca6'; const CUSTOM_GREEN = 'rgb(77, 162, 75)'; interface EventDefinitionProps { diff --git a/packages/website/ts/pages/documentation/method_block.tsx b/packages/website/ts/pages/documentation/method_block.tsx index 44e549211a..9505f2aa4b 100644 --- a/packages/website/ts/pages/documentation/method_block.tsx +++ b/packages/website/ts/pages/documentation/method_block.tsx @@ -1,8 +1,6 @@ import * as _ from 'lodash'; -import {Chip} from 'material-ui/Chip'; import {colors} from 'material-ui/styles'; import * as React from 'react'; -import * as ReactMarkdown from 'react-markdown'; import {Comment} from 'ts/pages/documentation/comment'; import {DocsInfo} from 'ts/pages/documentation/docs_info'; import {MethodSignature} from 'ts/pages/documentation/method_signature'; @@ -14,11 +12,9 @@ import { SolidityMethod, Styles, TypeDefinitionByName, - TypeDocNode, TypescriptMethod, } from 'ts/types'; import {typeDocUtils} from 'ts/utils/typedoc_utils'; -import {utils} from 'ts/utils/utils'; interface MethodBlockProps { method: SolidityMethod|TypescriptMethod; diff --git a/packages/website/ts/pages/documentation/method_signature.tsx b/packages/website/ts/pages/documentation/method_signature.tsx index 846c9fa4f3..df3b618144 100644 --- a/packages/website/ts/pages/documentation/method_signature.tsx +++ b/packages/website/ts/pages/documentation/method_signature.tsx @@ -42,6 +42,8 @@ export const MethodSignature: React.SFC = (props: MethodSi ); }; +MethodSignature.defaultProps = defaultProps; + function renderParameters( method: TypescriptMethod|SolidityMethod, docsInfo: DocsInfo, typeDefinitionByName?: TypeDefinitionByName, ) { diff --git a/packages/website/ts/pages/documentation/source_link.tsx b/packages/website/ts/pages/documentation/source_link.tsx index 74fc6d4d5f..9abf01706d 100644 --- a/packages/website/ts/pages/documentation/source_link.tsx +++ b/packages/website/ts/pages/documentation/source_link.tsx @@ -2,7 +2,6 @@ import * as _ from 'lodash'; import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Source} from 'ts/types'; -import {constants} from 'ts/utils/constants'; interface SourceLinkProps { source: Source; diff --git a/packages/website/ts/pages/documentation/type.tsx b/packages/website/ts/pages/documentation/type.tsx index c564429d0e..74b146d910 100644 --- a/packages/website/ts/pages/documentation/type.tsx +++ b/packages/website/ts/pages/documentation/type.tsx @@ -7,7 +7,6 @@ import {DocsInfo} from 'ts/pages/documentation/docs_info'; import {TypeDefinition} from 'ts/pages/documentation/type_definition'; import {Type as TypeDef, TypeDefinitionByName, TypeDocTypes} from 'ts/types'; import {constants} from 'ts/utils/constants'; -import {typeDocUtils} from 'ts/utils/typedoc_utils'; import {utils} from 'ts/utils/utils'; const BUILT_IN_TYPE_COLOR = '#e69d00'; @@ -48,10 +47,8 @@ interface TypeProps { // components (e.g when rendering the union type). export function Type(props: TypeProps): any { const type = props.type; - const isIntrinsic = type.typeDocType === TypeDocTypes.Intrinsic; const isReference = type.typeDocType === TypeDocTypes.Reference; const isArray = type.typeDocType === TypeDocTypes.Array; - const isStringLiteral = type.typeDocType === TypeDocTypes.StringLiteral; let typeNameColor = 'inherit'; let typeName: string|React.ReactNode; let typeArgs: React.ReactNode[] = []; diff --git a/packages/website/ts/pages/documentation/type_definition.tsx b/packages/website/ts/pages/documentation/type_definition.tsx index 17b182c70d..edcf7bf477 100644 --- a/packages/website/ts/pages/documentation/type_definition.tsx +++ b/packages/website/ts/pages/documentation/type_definition.tsx @@ -9,8 +9,6 @@ import {MethodSignature} from 'ts/pages/documentation/method_signature'; import {Type} from 'ts/pages/documentation/type'; import {AnchorTitle} from 'ts/pages/shared/anchor_title'; import {CustomType, CustomTypeChild, HeaderSizes, KindString, TypeDocTypes} from 'ts/types'; -import {constants} from 'ts/utils/constants'; -import {typeDocUtils} from 'ts/utils/typedoc_utils'; import {utils} from 'ts/utils/utils'; const KEYWORD_COLOR = '#a81ca6'; diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx index c53ed28b85..b3fe21da70 100644 --- a/packages/website/ts/pages/faq/faq.tsx +++ b/packages/website/ts/pages/faq/faq.tsx @@ -1,9 +1,7 @@ import * as _ from 'lodash'; -import RaisedButton from 'material-ui/RaisedButton'; import {colors} from 'material-ui/styles'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; -import {Link} from 'react-router-dom'; import {Footer} from 'ts/components/footer'; import {TopBar} from 'ts/components/top_bar'; import {Question} from 'ts/pages/faq/question'; diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index f3c46b8c78..7992c7a6c5 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -6,8 +6,7 @@ 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 {ScreenWidths, Styles, WebsitePaths} from 'ts/types'; -import {configs} from 'ts/utils/configs'; +import {ScreenWidths, WebsitePaths} from 'ts/types'; import {constants} from 'ts/utils/constants'; import {utils} from 'ts/utils/utils'; @@ -269,7 +268,7 @@ export class Landing extends React.Component { const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; const isMediumScreen = this.state.screenWidth === ScreenWidths.MD; const projectList = _.map(projects, (project: Project, i: number) => { - const colWidth = isSmallScreen ? 3 : isMediumScreen ? 4 : 2 - (i % 2); + const colWidth = isSmallScreen ? 3 : (isMediumScreen ? 4 : 2 - (i % 2)); return (
{ } private renderBuildingBlocksSection() { const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; - const underlineStyle: React.CSSProperties = { - height: isSmallScreen ? 18 : 23, - lineHeight: 'none', - borderBottom: '2px solid #979797', - }; const descriptionStyle: React.CSSProperties = { fontFamily: 'Roboto Mono', lineHeight: isSmallScreen ? 1.5 : 2, @@ -606,21 +600,6 @@ export class Landing extends React.Component { }); return assets; } - private renderLink(label: string, path: string, color: string, style?: React.CSSProperties) { - return ( -
- - {label} - -
- ); - } private renderInfoBoxes() { const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; const boxStyle: React.CSSProperties = { @@ -676,7 +655,6 @@ export class Landing extends React.Component { } private renderUseCases() { const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; - const isMediumScreen = this.state.screenWidth === ScreenWidths.MD; const useCases: UseCase[] = [ { diff --git a/packages/website/ts/pages/not_found.tsx b/packages/website/ts/pages/not_found.tsx index 075bcf91e0..df505792d8 100644 --- a/packages/website/ts/pages/not_found.tsx +++ b/packages/website/ts/pages/not_found.tsx @@ -1,6 +1,5 @@ import * as _ from 'lodash'; import * as React from 'react'; -import {Link} from 'react-router-dom'; import {Footer} from 'ts/components/footer'; import {TopBar} from 'ts/components/top_bar'; import {Styles} from 'ts/types'; diff --git a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx index cbb863f3e5..15ec443998 100644 --- a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx +++ b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx @@ -4,9 +4,8 @@ import {colors} from 'material-ui/styles'; import * as React from 'react'; import {Link as ScrollLink} from 'react-scroll'; import {VersionDropDown} from 'ts/pages/shared/version_drop_down'; -import {Docs, MenuSubsectionsBySection, Styles} from 'ts/types'; +import {MenuSubsectionsBySection, Styles} from 'ts/types'; import {constants} from 'ts/utils/constants'; -import {typeDocUtils} from 'ts/utils/typedoc_utils'; import {utils} from 'ts/utils/utils'; interface NestedSidebarMenuProps { diff --git a/packages/website/ts/pages/shared/version_drop_down.tsx b/packages/website/ts/pages/shared/version_drop_down.tsx index 4af9a834f9..e63ad19cd6 100644 --- a/packages/website/ts/pages/shared/version_drop_down.tsx +++ b/packages/website/ts/pages/shared/version_drop_down.tsx @@ -2,8 +2,6 @@ import * as _ from 'lodash'; import DropDownMenu from 'material-ui/DropDownMenu'; import MenuItem from 'material-ui/MenuItem'; import * as React from 'react'; -import {Docs} from 'ts/types'; -import {constants} from 'ts/utils/constants'; interface VersionDropDownProps { selectedVersion: string; diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index a0a1da21bc..cbcee65993 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -6,7 +6,6 @@ import { AssetToken, BlockchainErrs, Direction, - Fill, Order, ProviderType, ScreenWidths, diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index cae59af5fb..fb81dd9a9a 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -2,7 +2,6 @@ import BigNumber from 'bignumber.js'; import { ContractAddresses, Docs, - ExchangeContractErrs, Networks, PublicNodeUrlsByNetworkId, WebsitePaths, diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts index 594e3bae6e..ea210a3fa4 100644 --- a/packages/website/ts/utils/doc_utils.ts +++ b/packages/website/ts/utils/doc_utils.ts @@ -1,7 +1,6 @@ import findVersions = require('find-versions'); import * as _ from 'lodash'; import {DoxityDocObj, S3FileObject, TypeDocNode, VersionToFileName} from 'ts/types'; -import {constants} from 'ts/utils/constants'; import {utils} from 'ts/utils/utils'; import convert = require('xml-js'); diff --git a/packages/website/ts/utils/typedoc_utils.ts b/packages/website/ts/utils/typedoc_utils.ts index ff8060781c..9d3b9f8d7a 100644 --- a/packages/website/ts/utils/typedoc_utils.ts +++ b/packages/website/ts/utils/typedoc_utils.ts @@ -5,21 +5,17 @@ import { CustomTypeChild, DocAgnosticFormat, DocSection, - DocsMenu, IndexSignature, KindString, - MenuSubsectionsBySection, Parameter, Property, SectionsMap, Type, TypeDocNode, TypeDocType, - TypeDocTypes, TypeParameter, TypescriptMethod, } from 'ts/types'; -import {constants} from 'ts/utils/constants'; import {utils} from 'ts/utils/utils'; export const typeDocUtils = { diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 8b23b6a40e..abc2fb2d4d 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -1,7 +1,6 @@ import {ExchangeContractErrs, ZeroExError} from '0x.js'; import BigNumber from 'bignumber.js'; import deepEqual = require('deep-equal'); -import ethUtil = require('ethereumjs-util'); import isMobile = require('is-mobile'); import * as _ from 'lodash'; import * as moment from 'moment'; @@ -9,7 +8,6 @@ import { EtherscanLinkSuffixes, Networks, Order, - OrderParty, ScreenWidths, Side, SideToAssetToken, @@ -110,9 +108,9 @@ export const utils = { }, getColSize(items: number) { const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid - const colSize = 12 / items; + const colSize = bassCssGridSize / items; if (!_.isInteger(colSize)) { - throw new Error('Number of cols must be divisible by 12'); + throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`); } return colSize; }, From 818a50a3fb0b78992919e0366c73b36cbffa69b0 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 15 Dec 2017 15:32:10 +0100 Subject: [PATCH 014/125] Fix tests --- packages/0x.js/test/order_state_watcher_test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/0x.js/test/order_state_watcher_test.ts b/packages/0x.js/test/order_state_watcher_test.ts index 4d9a96502c..df32c7f141 100644 --- a/packages/0x.js/test/order_state_watcher_test.ts +++ b/packages/0x.js/test/order_state_watcher_test.ts @@ -234,6 +234,7 @@ describe('OrderStateWatcher', () => { const callback = reportCallbackErrors(done)((orderState: OrderState) => { done(); }); + zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0)); })().catch(done); From 94f40a54b42d467d9588862e11b2be45eb8f232c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 15 Dec 2017 11:05:53 -0600 Subject: [PATCH 015/125] Add default value for onConversionSuccessful --- .../website/ts/components/eth_weth_conversion_button.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index bf686d44b5..b017de27b5 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -32,6 +32,7 @@ export class EthWethConversionButton extends React.Component { public static defaultProps: Partial = { isDisabled: false, + onConversionSuccessful: _.noop, }; public constructor(props: EthWethConversionButtonProps) { super(props); @@ -100,9 +101,7 @@ export class EthWethConversionButton extends if (!this.props.isOutdatedWrappedEther) { this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance); } - if (!_.isUndefined(this.props.onConversionSuccessful)) { - this.props.onConversionSuccessful(); - } + this.props.onConversionSuccessful(); } catch (err) { const errMsg = '' + err; if (_.includes(errMsg, BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES)) { From 484dd4c33aa5786def1f0b159f5b51f99a585aa8 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 15 Dec 2017 11:06:18 -0600 Subject: [PATCH 016/125] Update name to be a call to action --- packages/website/ts/components/portal_menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/components/portal_menu.tsx b/packages/website/ts/components/portal_menu.tsx index e00fbd40ce..db8e3a9f17 100644 --- a/packages/website/ts/components/portal_menu.tsx +++ b/packages/website/ts/components/portal_menu.tsx @@ -56,7 +56,7 @@ export class PortalMenu extends React.Component - {this.renderMenuItemWithIcon('ETH wrapper', 'zmdi-circle-o')} + {this.renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')}
); From c8e52882ca556ea06696a56e312f69284f99aa29 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 15 Dec 2017 18:26:45 +0100 Subject: [PATCH 017/125] Fix redundant spaces --- .../remaining_fillable_calculator_test.ts | 10 +++---- .../test/utils/report_callback_errors.ts | 2 +- packages/assert/test/assert_test.ts | 30 +++++++++---------- .../test/utils/report_callback_errors.ts | 2 +- packages/tslint-config/package.json | 7 +++-- packages/tslint-config/tslint.json | 4 ++- .../website/ts/components/token_balances.tsx | 2 +- packages/website/ts/components/top_bar.tsx | 2 +- yarn.lock | 27 +++++++++++++++++ 9 files changed, 58 insertions(+), 28 deletions(-) diff --git a/packages/0x.js/test/remaining_fillable_calculator_test.ts b/packages/0x.js/test/remaining_fillable_calculator_test.ts index 47ba13cd79..95ef0a4f1a 100644 --- a/packages/0x.js/test/remaining_fillable_calculator_test.ts +++ b/packages/0x.js/test/remaining_fillable_calculator_test.ts @@ -2,11 +2,11 @@ import BigNumber from 'bignumber.js'; import * as chai from 'chai'; import 'mocha'; -import { ZeroEx } from '../src/0x'; -import { RemainingFillableCalculator } from '../src/order_watcher/remaining_fillable_calculator'; -import { ECSignature, SignedOrder } from '../src/types'; +import {ZeroEx} from '../src/0x'; +import {RemainingFillableCalculator} from '../src/order_watcher/remaining_fillable_calculator'; +import {ECSignature, SignedOrder} from '../src/types'; -import { chaiSetup } from './utils/chai_setup'; +import {chaiSetup} from './utils/chai_setup'; chaiSetup.configure(); const expect = chai.expect; @@ -26,7 +26,7 @@ describe('RemainingFillableCalculator', () => { const decimals: number = 4; const zero: BigNumber = new BigNumber(0); const zeroAddress = '0x0'; - const signature: ECSignature = { v: 27, r: '', s: ''}; + const signature: ECSignature = {v: 27, r: '', s: ''}; beforeEach(async () => { [makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals), ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals), diff --git a/packages/0x.js/test/utils/report_callback_errors.ts b/packages/0x.js/test/utils/report_callback_errors.ts index 8a8f4d966b..0aaef3f05b 100644 --- a/packages/0x.js/test/utils/report_callback_errors.ts +++ b/packages/0x.js/test/utils/report_callback_errors.ts @@ -1,4 +1,4 @@ -import { DoneCallback } from '../../src/types'; +import {DoneCallback} from '../../src/types'; export const reportCallbackErrors = (done: DoneCallback) => { return (f: (...args: any[]) => void) => { diff --git a/packages/assert/test/assert_test.ts b/packages/assert/test/assert_test.ts index 5fa96e49a0..6a9efc7482 100644 --- a/packages/assert/test/assert_test.ts +++ b/packages/assert/test/assert_test.ts @@ -25,7 +25,7 @@ describe('Assertions', () => { 'test', 42, false, - { random: 'test' }, + {random: 'test'}, undefined, ]; invalidInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.throw()); @@ -43,7 +43,7 @@ describe('Assertions', () => { 'test', 42, false, - { random: 'test' }, + {random: 'test'}, ]; invalidInputs.forEach(input => expect(assert.isUndefined.bind(assert, input, variableName)).to.throw()); }); @@ -60,7 +60,7 @@ describe('Assertions', () => { const invalidInputs = [ 42, false, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), ]; @@ -79,7 +79,7 @@ describe('Assertions', () => { const invalidInputs = [ 42, false, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), ]; @@ -98,7 +98,7 @@ describe('Assertions', () => { const invalidInputs = [ 42, false, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), '0x61a3ed31B43c8780e905a260a35faYfEc527be7516aa11c0256729b5b351bc33', @@ -121,7 +121,7 @@ describe('Assertions', () => { const invalidInputs = [ 42, false, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), '0x6FFFd0ae3f7d88c9b4925323f54c6e4b2918c5fd', @@ -150,7 +150,7 @@ describe('Assertions', () => { const invalidInputs = [ 42, false, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), ]; @@ -192,7 +192,7 @@ describe('Assertions', () => { it('should throw for invalid input', () => { const invalidInputs = [ false, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), ]; @@ -210,7 +210,7 @@ describe('Assertions', () => { it('should throw for invalid input', () => { const invalidInputs = [ 42, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), ]; @@ -220,8 +220,8 @@ describe('Assertions', () => { describe('#isWeb3Provider', () => { it('should not throw for valid input', () => { const validInputs = [ - { send: () => 45 }, - { sendAsync: () => 45 }, + {send: () => 45}, + {sendAsync: () => 45}, ]; validInputs.forEach(input => expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.not.throw(), @@ -230,7 +230,7 @@ describe('Assertions', () => { it('should throw for invalid input', () => { const invalidInputs = [ 42, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), ]; @@ -253,7 +253,7 @@ describe('Assertions', () => { it('should throw for invalid input', () => { const invalidInputs = [ 42, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), ]; @@ -277,7 +277,7 @@ describe('Assertions', () => { it('should throw for invalid input', () => { const invalidInputs = [ 42, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), 'ws://www.api.example-relayer.net', @@ -309,7 +309,7 @@ describe('Assertions', () => { it('should throw for invalid input', () => { const invalidInputs = [ 42, - { random: 'test' }, + {random: 'test'}, undefined, new BigNumber(45), 'www.google.com', diff --git a/packages/subproviders/test/utils/report_callback_errors.ts b/packages/subproviders/test/utils/report_callback_errors.ts index 8a8f4d966b..0aaef3f05b 100644 --- a/packages/subproviders/test/utils/report_callback_errors.ts +++ b/packages/subproviders/test/utils/report_callback_errors.ts @@ -1,4 +1,4 @@ -import { DoneCallback } from '../../src/types'; +import {DoneCallback} from '../../src/types'; export const reportCallbackErrors = (done: DoneCallback) => { return (f: (...args: any[]) => void) => { diff --git a/packages/tslint-config/package.json b/packages/tslint-config/package.json index 3b320e774e..888ff675a8 100644 --- a/packages/tslint-config/package.json +++ b/packages/tslint-config/package.json @@ -4,9 +4,9 @@ "description": "Lint rules related to 0xProject for TSLint", "main": "tslint.json", "scripts": { - "build": "tsc", - "clean": "shx rm -rf lib", - "lint": "tslint --project . 'rules/**/*.ts'" + "build": "tsc", + "clean": "shx rm -rf lib", + "lint": "tslint --project . 'rules/**/*.ts'" }, "files": [ "tslint.js", @@ -37,6 +37,7 @@ "@types/lodash": "^4.14.86", "shx": "^0.2.2", "tslint": "5.8.0", + "tslint-eslint-rules": "^4.1.1", "typescript": "~2.6.1" }, "dependencies": { diff --git a/packages/tslint-config/tslint.json b/packages/tslint-config/tslint.json index 91185bddbb..acb6876c34 100644 --- a/packages/tslint-config/tslint.json +++ b/packages/tslint-config/tslint.json @@ -1,7 +1,8 @@ { "extends": [ "tslint:latest", - "tslint-react" + "tslint-react", + "tslint-eslint-rules" ], "rules": { "adjacent-overload-signatures": true, @@ -54,6 +55,7 @@ "no-unused-variable": [true, {"ignore-pattern": "^_\\d*"}], "no-implicit-dependencies": [true, "dev"], "number-literal-format": true, + "object-curly-spacing": [true, "never"], "object-literal-sort-keys": false, "ordered-imports": [ true, diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 4781145167..b68081c37d 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -576,7 +576,7 @@ export class TokenBalances extends React.Component +

We apologize -- Dharma loan requests are not available on mobile yet. Please try again through your desktop browser.

diff --git a/packages/website/ts/components/top_bar.tsx b/packages/website/ts/components/top_bar.tsx index 488eef9141..27de496208 100644 --- a/packages/website/ts/components/top_bar.tsx +++ b/packages/website/ts/components/top_bar.tsx @@ -124,7 +124,7 @@ export class TopBar extends React.Component { className="text-decoration-none" href={constants.GITHUB_URL} > - + , Date: Fri, 15 Dec 2017 12:47:16 -0600 Subject: [PATCH 018/125] Fix linter errors --- packages/website/ts/components/eth_wrappers.tsx | 7 ------- packages/website/ts/components/token_balances.tsx | 5 ----- packages/website/ts/utils/configs.ts | 1 - 3 files changed, 13 deletions(-) diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index ccbed41882..58b73b11cf 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -2,8 +2,6 @@ import {ZeroEx} from '0x.js'; import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import Divider from 'material-ui/Divider'; -import Paper from 'material-ui/Paper'; -import RaisedButton from 'material-ui/RaisedButton'; import {colors} from 'material-ui/styles'; import { Table, @@ -17,8 +15,6 @@ import * as moment from 'moment'; import * as React from 'react'; import {Blockchain} from 'ts/blockchain'; import {EthWethConversionButton} from 'ts/components/eth_weth_conversion_button'; -import {LifeCycleRaisedButton} from 'ts/components/ui/lifecycle_raised_button'; -import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; import {Dispatcher} from 'ts/redux/dispatcher'; import { OutdatedWrappedEtherByNetworkId, @@ -30,8 +26,6 @@ import { } from 'ts/types'; import {configs} from 'ts/utils/configs'; import {constants} from 'ts/utils/constants'; -import {errorReporter} from 'ts/utils/error_reporter'; -import {utils} from 'ts/utils/utils'; const PRECISION = 5; const DATE_FORMAT = 'D/M/YY'; @@ -39,7 +33,6 @@ const LIGHT_GRAY = '#A5A5A5'; const ICON_DIMENSION = 40; const ETHER_ICON_PATH = '/images/ether.png'; const OUTDATED_WETH_ICON_PATH = '/images/wrapped_eth_gray.png'; -const ETHER_TOKEN_SYMBOL = 'WETH'; interface OutdatedWETHAddressToIsStateLoaded { [address: string]: boolean; diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index ab232326a9..db4a35062c 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -655,11 +655,6 @@ export class TokenBalances extends React.Component Date: Fri, 15 Dec 2017 13:01:45 -0600 Subject: [PATCH 019/125] Two mysteriously missing imports --- packages/website/ts/components/order_json.tsx | 1 + packages/website/ts/containers/generate_order_form.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/website/ts/components/order_json.tsx b/packages/website/ts/components/order_json.tsx index c499ee9c2e..83f25fd5ac 100644 --- a/packages/website/ts/components/order_json.tsx +++ b/packages/website/ts/components/order_json.tsx @@ -1,5 +1,6 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; +import {Paper} from 'material-ui/Paper'; import TextField from 'material-ui/TextField'; import * as React from 'react'; import {CopyIcon} from 'ts/components/ui/copy_icon'; diff --git a/packages/website/ts/containers/generate_order_form.tsx b/packages/website/ts/containers/generate_order_form.tsx index 134c84859c..a47895d942 100644 --- a/packages/website/ts/containers/generate_order_form.tsx +++ b/packages/website/ts/containers/generate_order_form.tsx @@ -1,3 +1,4 @@ +import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import * as React from 'react'; import {connect} from 'react-redux'; From fe8f2d87c79cb25a7879c2e737dbdd64b7c40f60 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 15 Dec 2017 14:07:14 -0600 Subject: [PATCH 020/125] Temporary hack to alleviate issues with updating tokenRegistry with new WETH address --- packages/website/ts/blockchain.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 331bafa717..6877a301a8 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -592,9 +592,22 @@ export class Blockchain { // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens // TODO: Refactor this out and pull the iconUrl directly from the TokenRegistry const iconUrl = constants.iconUrlBySymbol[t.symbol]; + // HACK: Temporarily we hijack the WETH addresses fetched from the tokenRegistry + // so that we can take our time with actually updating it. This ensures that when + // we deploy the new WETH page, everyone will re-fill their trackedTokens with the + // new canonical WETH. + // TODO: Remove this hack once we've updated the TokenRegistries + let address = t.address; + if (t.symbol === 'WETH') { + if (this.networkId === 1) { + address = '0xe495bcacaf29a0eb00fb67b86e9cd2a994dd55d8'; + } else if (this.networkId === 42) { + address = '0x739e78d6bebbdf24105a5145fa04436589d1cbd9'; + } + } const token: Token = { iconUrl, - address: t.address, + address, name: t.name, symbol: t.symbol, decimals: t.decimals, From 4a73c05435a653565a92aedfbf56d45997f0673f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 00:19:15 -0600 Subject: [PATCH 021/125] Fix documentation issue where `unsubscribeAll` shown as method on every contractWrapper instance even though it's only used by Exchange and Token wrappers. --- packages/0x.js/src/contract_wrappers/contract_wrapper.ts | 5 +---- packages/0x.js/src/contract_wrappers/exchange_wrapper.ts | 6 ++++++ packages/0x.js/src/contract_wrappers/token_wrapper.ts | 6 ++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts index a796dc1ec6..46916ebf47 100644 --- a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts @@ -50,10 +50,7 @@ export class ContractWrapper { this._onLogAddedSubscriptionToken = undefined; this._onLogRemovedSubscriptionToken = undefined; } - /** - * Cancels all existing subscriptions - */ - public 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/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts index 9bed400799..3ca5695c44 100644 --- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts @@ -607,6 +607,12 @@ export class ExchangeWrapper extends ContractWrapper { public unsubscribe(subscriptionToken: string): void { this._unsubscribe(subscriptionToken); } + /** + * Cancels all existing subscriptions + */ + public unsubscribeAll(): void { + super.unsubscribeAll(); + } /** * Gets historical logs without creating a subscription * @param eventName The exchange contract event you would like to subscribe to. diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts index d1553fa7b1..eccb748713 100644 --- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts @@ -281,6 +281,12 @@ export class TokenWrapper extends ContractWrapper { public unsubscribe(subscriptionToken: string): void { this._unsubscribe(subscriptionToken); } + /** + * Cancels all existing subscriptions + */ + public unsubscribeAll(): void { + super.unsubscribeAll(); + } /** * Gets historical logs without creating a subscription * @param tokenAddress An address of the token that emmited the logs. From 1f748afed96ed40d1ee61b970e8c6fe2a75fa382 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 00:32:53 -0600 Subject: [PATCH 022/125] Modify the etherToken wrapper methods to accept an etherTokenAddress as the first arg. Since it is becoming apparent we will be updating the canonical WETH contract, we want users of 0x.js to be able to interact with n number of etherTokens without re-instantiating for each one. --- packages/0x.js/src/0x.ts | 2 +- .../contract_wrappers/ether_token_wrapper.ts | 41 ++++++------------- .../src/schemas/zero_ex_config_schema.ts | 1 - packages/0x.js/src/types.ts | 2 - packages/0x.js/test/0x.js_test.ts | 8 ---- .../0x.js/test/ether_token_wrapper_test.ts | 13 +++--- 6 files changed, 21 insertions(+), 46 deletions(-) diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts index 5a2d6cb059..41fefb9934 100644 --- a/packages/0x.js/src/0x.ts +++ b/packages/0x.js/src/0x.ts @@ -199,7 +199,7 @@ export class ZeroEx { this._web3Wrapper, config.networkId, config.tokenRegistryContractAddress, ); this.etherToken = new EtherTokenWrapper( - this._web3Wrapper, config.networkId, this.token, config.etherTokenContractAddress, + this._web3Wrapper, config.networkId, this.token, ); this.orderStateWatcher = new OrderStateWatcher( this._web3Wrapper, this._abiDecoder, this.token, this.exchange, config.orderWatcherConfig, 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 896cfde3d7..a6acbe45d8 100644 --- a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts @@ -17,24 +17,22 @@ import {TokenWrapper} from './token_wrapper'; export class EtherTokenWrapper extends ContractWrapper { private _etherTokenContractIfExists?: EtherTokenContract; private _tokenWrapper: TokenWrapper; - private _contractAddressIfExists?: string; - constructor(web3Wrapper: Web3Wrapper, networkId: number, tokenWrapper: TokenWrapper, - contractAddressIfExists?: string) { + constructor(web3Wrapper: Web3Wrapper, networkId: number, tokenWrapper: TokenWrapper) { super(web3Wrapper, networkId); this._tokenWrapper = tokenWrapper; - this._contractAddressIfExists = contractAddressIfExists; } /** * Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens * to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1 * for ETH. - * @param amountInWei Amount of ETH in Wei the caller wishes to deposit. - * @param depositor The hex encoded user Ethereum address that would like to make the deposit. - * @param txOpts Transaction parameters. + * @param etherTokenAddress EtherToken address you wish to deposit into. + * @param amountInWei Amount of ETH in Wei the caller wishes to deposit. + * @param depositor The hex encoded user Ethereum address that would like to make the deposit. + * @param txOpts Transaction parameters. * @return Transaction hash. */ public async depositAsync( - amountInWei: BigNumber, depositor: string, txOpts: TransactionOpts = {}, + etherTokenAddress: string, amountInWei: BigNumber, depositor: string, txOpts: TransactionOpts = {}, ): Promise { assert.isValidBaseUnitAmount('amountInWei', amountInWei); await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper); @@ -42,7 +40,7 @@ export class EtherTokenWrapper extends ContractWrapper { const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor); assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit); - const wethContract = await this._getEtherTokenContractAsync(); + const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress); const txHash = await wethContract.deposit.sendTransactionAsync({ from: depositor, value: amountInWei, @@ -54,22 +52,22 @@ export class EtherTokenWrapper extends ContractWrapper { /** * Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the * equivalent number of wrapped ETH tokens. + * @param etherTokenAddress EtherToken address you wish to withdraw from. * @param amountInWei Amount of ETH in Wei the caller wishes to withdraw. * @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl. * @param txOpts Transaction parameters. * @return Transaction hash. */ public async withdrawAsync( - amountInWei: BigNumber, withdrawer: string, txOpts: TransactionOpts = {}, + etherTokenAddress: string, amountInWei: BigNumber, withdrawer: string, txOpts: TransactionOpts = {}, ): Promise { assert.isValidBaseUnitAmount('amountInWei', amountInWei); await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper); - const wethContractAddress = this.getContractAddress(); - const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(wethContractAddress, withdrawer); + const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(etherTokenAddress, withdrawer); assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal); - const wethContract = await this._getEtherTokenContractAsync(); + const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress); const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, { from: withdrawer, gas: txOpts.gasLimit, @@ -77,25 +75,12 @@ export class EtherTokenWrapper extends ContractWrapper { }); return txHash; } - /** - * Retrieves the Wrapped Ether token contract address - * @return The Wrapped Ether token contract address - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress( - artifacts.EtherTokenArtifact, this._contractAddressIfExists, - ); - return contractAddress; - } private _invalidateContractInstance(): void { delete this._etherTokenContractIfExists; } - private async _getEtherTokenContractAsync(): Promise { - if (!_.isUndefined(this._etherTokenContractIfExists)) { - return this._etherTokenContractIfExists; - } + private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise { const web3ContractInstance = await this._instantiateContractIfExistsAsync( - artifacts.EtherTokenArtifact, this._contractAddressIfExists, + artifacts.EtherTokenArtifact, etherTokenAddress, ); const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults()); this._etherTokenContractIfExists = contractInstance; diff --git a/packages/0x.js/src/schemas/zero_ex_config_schema.ts b/packages/0x.js/src/schemas/zero_ex_config_schema.ts index 121d812574..e0d985749f 100644 --- a/packages/0x.js/src/schemas/zero_ex_config_schema.ts +++ b/packages/0x.js/src/schemas/zero_ex_config_schema.ts @@ -8,7 +8,6 @@ export const zeroExConfigSchema = { gasPrice: {$ref: '/Number'}, exchangeContractAddress: {$ref: '/Address'}, tokenRegistryContractAddress: {$ref: '/Address'}, - etherTokenContractAddress: {$ref: '/Address'}, orderWatcherConfig: { type: 'object', properties: { diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index f33e05bb8b..fb3cfa6dff 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -268,7 +268,6 @@ export interface OrderStateWatcherConfig { * gasPrice: Gas price to use with every transaction * exchangeContractAddress: The address of an exchange contract to use * tokenRegistryContractAddress: The address of a token registry contract to use - * etherTokenContractAddress: The address of an ether token contract to use * tokenTransferProxyContractAddress: The address of the token transfer proxy contract to use * orderWatcherConfig: All the configs related to the orderWatcher */ @@ -277,7 +276,6 @@ export interface ZeroExConfig { gasPrice?: BigNumber; exchangeContractAddress?: string; tokenRegistryContractAddress?: string; - etherTokenContractAddress?: string; tokenTransferProxyContractAddress?: string; orderWatcherConfig?: OrderStateWatcherConfig; } diff --git a/packages/0x.js/test/0x.js_test.ts b/packages/0x.js/test/0x.js_test.ts index e1ceb12c85..c5aa1926f4 100644 --- a/packages/0x.js/test/0x.js_test.ts +++ b/packages/0x.js/test/0x.js_test.ts @@ -244,14 +244,6 @@ describe('ZeroEx library', () => { const zeroExWithWrongExchangeAddress = new ZeroEx(web3.currentProvider, zeroExConfig); expect(zeroExWithWrongExchangeAddress.exchange.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS); }); - it('allows to specify ether token contract address', async () => { - const zeroExConfig = { - etherTokenContractAddress: ZeroEx.NULL_ADDRESS, - networkId: constants.TESTRPC_NETWORK_ID, - }; - const zeroExWithWrongEtherTokenAddress = new ZeroEx(web3.currentProvider, zeroExConfig); - expect(zeroExWithWrongEtherTokenAddress.etherToken.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS); - }); it('allows to specify token registry token contract address', async () => { const zeroExConfig = { tokenRegistryContractAddress: ZeroEx.NULL_ADDRESS, diff --git a/packages/0x.js/test/ether_token_wrapper_test.ts b/packages/0x.js/test/ether_token_wrapper_test.ts index e0d738f840..b5ed193082 100644 --- a/packages/0x.js/test/ether_token_wrapper_test.ts +++ b/packages/0x.js/test/ether_token_wrapper_test.ts @@ -5,6 +5,7 @@ import 'mocha'; import * as Web3 from 'web3'; import {ZeroEx, ZeroExError} from '../src'; +import {artifacts} from '../src/artifacts'; import {chaiSetup} from './utils/chai_setup'; import {constants} from './utils/constants'; @@ -38,7 +39,7 @@ describe('EtherTokenWrapper', () => { zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig); userAddresses = await zeroEx.getAvailableAddressesAsync(); addressWithETH = userAddresses[0]; - wethContractAddress = zeroEx.etherToken.getContractAddress(); + wethContractAddress = (zeroEx.etherToken as any)._getContractAddress(artifacts.EtherTokenArtifact); depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5)); decimalPlaces = 7; }); @@ -55,7 +56,7 @@ describe('EtherTokenWrapper', () => { expect(preETHBalance).to.be.bignumber.gt(0); expect(preWETHBalance).to.be.bignumber.equal(0); - const txHash = await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); + const txHash = await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH); await zeroEx.awaitTransactionMinedAsync(txHash); const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); @@ -73,7 +74,7 @@ describe('EtherTokenWrapper', () => { const overETHBalanceinWei = preETHBalance.add(extraETHBalance); return expect( - zeroEx.etherToken.depositAsync(overETHBalanceinWei, addressWithETH), + zeroEx.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH), ).to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit); }); }); @@ -81,7 +82,7 @@ describe('EtherTokenWrapper', () => { it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => { const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); - await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); + await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH); const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount); const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); @@ -90,7 +91,7 @@ describe('EtherTokenWrapper', () => { expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI); expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount); - const txHash = await zeroEx.etherToken.withdrawAsync(depositWeiAmount, addressWithETH); + const txHash = await zeroEx.etherToken.withdrawAsync(wethContractAddress, depositWeiAmount, addressWithETH); await zeroEx.awaitTransactionMinedAsync(txHash); const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); @@ -108,7 +109,7 @@ describe('EtherTokenWrapper', () => { const overWETHBalance = preWETHBalance.add(999999999); return expect( - zeroEx.etherToken.withdrawAsync(overWETHBalance, addressWithETH), + zeroEx.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH), ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal); }); }); From ac78c64f9ec0c1894dce051154a675428c5540e0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 00:40:19 -0600 Subject: [PATCH 023/125] Fix tests in contracts --- packages/contracts/test/ts/ether_token.ts | 9 ++++----- packages/contracts/test/ts/ether_token_v2.ts | 15 +++++++-------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/contracts/test/ts/ether_token.ts b/packages/contracts/test/ts/ether_token.ts index 2f9df59a41..99630583fb 100644 --- a/packages/contracts/test/ts/ether_token.ts +++ b/packages/contracts/test/ts/ether_token.ts @@ -28,7 +28,6 @@ contract('EtherToken', (accounts: string[]) => { etherTokenAddress = EtherToken.address; zeroEx = new ZeroEx(web3.currentProvider, { gasPrice, - etherTokenContractAddress: etherTokenAddress, networkId: constants.TESTRPC_NETWORK_ID, }); }); @@ -45,7 +44,7 @@ contract('EtherToken', (accounts: string[]) => { const initEthBalance = await getEthBalanceAsync(account); const ethToDeposit = initEthBalance.plus(1); - return expect(zeroEx.etherToken.depositAsync(ethToDeposit, account)) + return expect(zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account)) .to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit); }); @@ -55,7 +54,7 @@ contract('EtherToken', (accounts: string[]) => { const ethToDeposit = new BigNumber(web3.toWei(1, 'ether')); - const txHash = await zeroEx.etherToken.depositAsync(ethToDeposit, account); + const txHash = await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account); const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); const ethSpentOnGas = gasPrice.times(receipt.gasUsed); @@ -72,7 +71,7 @@ contract('EtherToken', (accounts: string[]) => { const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); const ethTokensToWithdraw = initEthTokenBalance.plus(1); - return expect(zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account)) + return expect(zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account)) .to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal); }); @@ -81,7 +80,7 @@ contract('EtherToken', (accounts: string[]) => { const initEthBalance = await getEthBalanceAsync(account); const ethTokensToWithdraw = initEthTokenBalance; expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0); - const txHash = await zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account, { + const txHash = await zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account, { gasLimit: constants.MAX_ETHERTOKEN_WITHDRAW_GAS, }); const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); diff --git a/packages/contracts/test/ts/ether_token_v2.ts b/packages/contracts/test/ts/ether_token_v2.ts index 61ac4cb578..480c4035bc 100644 --- a/packages/contracts/test/ts/ether_token_v2.ts +++ b/packages/contracts/test/ts/ether_token_v2.ts @@ -28,7 +28,6 @@ contract('EtherTokenV2', (accounts: string[]) => { etherTokenAddress = etherToken.address; zeroEx = new ZeroEx(web3.currentProvider, { gasPrice, - etherTokenContractAddress: etherTokenAddress, networkId: constants.TESTRPC_NETWORK_ID, }); }); @@ -45,7 +44,7 @@ contract('EtherTokenV2', (accounts: string[]) => { const initEthBalance = await getEthBalanceAsync(account); const ethToDeposit = initEthBalance.plus(1); - return expect(zeroEx.etherToken.depositAsync(ethToDeposit, account)) + return expect(zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account)) .to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit); }); @@ -55,7 +54,7 @@ contract('EtherTokenV2', (accounts: string[]) => { const ethToDeposit = new BigNumber(web3.toWei(1, 'ether')); - const txHash = await zeroEx.etherToken.depositAsync(ethToDeposit, account); + const txHash = await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account); const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); const ethSpentOnGas = gasPrice.times(receipt.gasUsed); @@ -69,7 +68,7 @@ contract('EtherTokenV2', (accounts: string[]) => { it('should log 1 event with correct arguments', async () => { const ethToDeposit = new BigNumber(web3.toWei(1, 'ether')); - const txHash = await zeroEx.etherToken.depositAsync(ethToDeposit, account); + const txHash = await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account); const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); const logs = receipt.logs; @@ -88,14 +87,14 @@ contract('EtherTokenV2', (accounts: string[]) => { describe('withdraw', () => { beforeEach(async () => { const ethToDeposit = new BigNumber(web3.toWei(1, 'ether')); - await zeroEx.etherToken.depositAsync(ethToDeposit, account); + await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account); }); it('should throw if caller attempts to withdraw greater than caller balance', async () => { const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); const ethTokensToWithdraw = initEthTokenBalance.plus(1); - return expect(zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account)) + return expect(zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account)) .to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal); }); @@ -104,7 +103,7 @@ contract('EtherTokenV2', (accounts: string[]) => { const initEthBalance = await getEthBalanceAsync(account); const ethTokensToWithdraw = initEthTokenBalance; expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0); - const txHash = await zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account, { + const txHash = await zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account, { gasLimit: constants.MAX_ETHERTOKEN_WITHDRAW_GAS, }); const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); @@ -122,7 +121,7 @@ contract('EtherTokenV2', (accounts: string[]) => { const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); const ethTokensToWithdraw = initEthTokenBalance; expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0); - const txHash = await zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account, { + const txHash = await zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account, { gasLimit: constants.MAX_ETHERTOKEN_WITHDRAW_GAS, }); const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); From 5664333490474d1d9a793e887864f125869bf6fa Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 00:44:34 -0600 Subject: [PATCH 024/125] Since sending the error report could take some time, we first trigger `onError` so that the user gets notified immediately --- packages/website/ts/components/send_button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx index b3fd2aebae..d8d3c7f56d 100644 --- a/packages/website/ts/components/send_button.tsx +++ b/packages/website/ts/components/send_button.tsx @@ -76,8 +76,8 @@ export class SendButton extends React.Component Date: Sun, 17 Dec 2017 01:07:28 -0600 Subject: [PATCH 025/125] Add entry to CHANGELOG --- packages/0x.js/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 963b3d56ca..0ef433fe99 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +v0.28.0 - _TBD_ +------------------------ +* Add `etherTokenAddress` arg to `depositAsync` and `withdrawAsync` methods on `EtherToken` wrapper. (#267) + v0.27.1 - _November 28, 2017_ ------------------------ * Export `TransactionOpts` type From 51af6de6249ac8d7656052f2a7198ae4341061b6 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 12:36:50 -0600 Subject: [PATCH 026/125] Update to passing etherTokenAddress into withdraw and deposit calls --- packages/website/ts/blockchain.ts | 8 ++++---- .../website/ts/components/eth_weth_conversion_button.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 6877a301a8..a42b19cffe 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -388,18 +388,18 @@ export class Blockchain { const balance = await this.web3Wrapper.getBalanceInEthAsync(owner); return balance; } - public async convertEthToWrappedEthTokensAsync(amount: BigNumber): Promise { + public async convertEthToWrappedEthTokensAsync(etherTokenAddress: string, amount: BigNumber): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); - const txHash = await this.zeroEx.etherToken.depositAsync(amount, this.userAddress); + const txHash = await this.zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this.userAddress); await this.showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } - public async convertWrappedEthTokensToEthAsync(amount: BigNumber): Promise { + public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); - const txHash = await this.zeroEx.etherToken.withdrawAsync(amount, this.userAddress); + const txHash = await this.zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this.userAddress); await this.showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } public async doesContractExistAtAddressAsync(address: string) { diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index b017de27b5..c8a279de97 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -88,12 +88,12 @@ export class EthWethConversionButton extends let balance = tokenState.balance; try { if (direction === Side.deposit) { - await this.props.blockchain.convertEthToWrappedEthTokensAsync(value); + await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value); const ethAmount = ZeroEx.toUnitAmount(value, constants.ETH_DECIMAL_PLACES); this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`); balance = balance.plus(value); } else { - await this.props.blockchain.convertWrappedEthTokensToEthAsync(value); + 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); From b640e2cc4990e83511e1f796a06cdc890ceec396 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 12:40:04 -0600 Subject: [PATCH 027/125] Update website calls to deposit/withdraw --- packages/website/ts/blockchain.ts | 8 ++++---- .../website/ts/components/eth_weth_conversion_button.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 6877a301a8..a42b19cffe 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -388,18 +388,18 @@ export class Blockchain { const balance = await this.web3Wrapper.getBalanceInEthAsync(owner); return balance; } - public async convertEthToWrappedEthTokensAsync(amount: BigNumber): Promise { + public async convertEthToWrappedEthTokensAsync(etherTokenAddress: string, amount: BigNumber): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); - const txHash = await this.zeroEx.etherToken.depositAsync(amount, this.userAddress); + const txHash = await this.zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this.userAddress); await this.showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } - public async convertWrappedEthTokensToEthAsync(amount: BigNumber): Promise { + public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); - const txHash = await this.zeroEx.etherToken.withdrawAsync(amount, this.userAddress); + const txHash = await this.zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this.userAddress); await this.showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } public async doesContractExistAtAddressAsync(address: string) { diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index b017de27b5..c8a279de97 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -88,12 +88,12 @@ export class EthWethConversionButton extends let balance = tokenState.balance; try { if (direction === Side.deposit) { - await this.props.blockchain.convertEthToWrappedEthTokensAsync(value); + await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value); const ethAmount = ZeroEx.toUnitAmount(value, constants.ETH_DECIMAL_PLACES); this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`); balance = balance.plus(value); } else { - await this.props.blockchain.convertWrappedEthTokensToEthAsync(value); + 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); From fd3e8cd8133510a02aa6779a780c20018bebabe0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 13:20:27 -0600 Subject: [PATCH 028/125] Add convenience `rebuild` command --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index e6230bf189..e702c1d90e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "scripts": { "testrpc": "testrpc -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"", "lerna:run": "lerna run", + "lerna:rebuild": "lerna run clean; lerna run build;", "lerna:publish": "yarn install; lerna run clean; lerna run build; lerna publish --registry=https://registry.npmjs.org/" }, "config": { From edbe6915e07a516f5fad2a38eb8ffe96a7b28318 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 13:22:54 -0600 Subject: [PATCH 029/125] Fixed https://github.com/0xProject/wiki/issues/19 by disabling re-rendering of markdownCodeBlock renderer if props haven't updated --- .../ts/pages/shared/markdown_code_block.tsx | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/website/ts/pages/shared/markdown_code_block.tsx b/packages/website/ts/pages/shared/markdown_code_block.tsx index 621e5b6062..aded15f0ce 100644 --- a/packages/website/ts/pages/shared/markdown_code_block.tsx +++ b/packages/website/ts/pages/shared/markdown_code_block.tsx @@ -7,14 +7,23 @@ interface MarkdownCodeBlockProps { language: string; } -export function MarkdownCodeBlock(props: MarkdownCodeBlockProps) { - return ( - - - {props.literal} - - - ); +interface MarkdownCodeBlockState {} + +export class MarkdownCodeBlock extends React.Component { + // Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying + // to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed. + public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) { + return nextProps.literal !== this.props.literal || nextProps.language !== this.props.language; + } + public render() { + return ( + + + {this.props.literal} + + + ); + } } From 9e914be975e20771e0f442eb4b4b025d44eb1994 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 14:14:23 -0600 Subject: [PATCH 030/125] Link all EtherTokens to Etherscan and add address tooltip --- .../website/ts/components/eth_wrappers.tsx | 73 ++++++++++++++----- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 58b73b11cf..a6a1df7511 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -13,10 +13,12 @@ import { } from 'material-ui/Table'; import * as moment from 'moment'; import * as React from 'react'; +import ReactTooltip = require('react-tooltip'); import {Blockchain} from 'ts/blockchain'; import {EthWethConversionButton} from 'ts/components/eth_weth_conversion_button'; import {Dispatcher} from 'ts/redux/dispatcher'; import { + EtherscanLinkSuffixes, OutdatedWrappedEtherByNetworkId, Side, Token, @@ -26,6 +28,7 @@ import { } from 'ts/types'; import {configs} from 'ts/utils/configs'; import {constants} from 'ts/utils/constants'; +import {utils} from 'ts/utils/utils'; const PRECISION = 5; const DATE_FORMAT = 'D/M/YY'; @@ -85,6 +88,10 @@ export class EthWrappers extends React.Component
@@ -131,8 +138,11 @@ export class EthWrappers extends React.Component -
- Ether +
+ ETH
@@ -153,15 +163,7 @@ export class EthWrappers extends React.Component -
- -
- Wrapped Ether -
-
+ {this.renderTokenLink(tokenLabel, etherscanUrl)}
{wethBalance.toFixed(PRECISION)} WETH @@ -264,18 +266,14 @@ export class EthWrappers extends React.Component -
- -
- {dateRange} -
-
+ {this.renderTokenLink(tokenLabel, etherscanUrl)}
{isStateLoaded ? @@ -301,6 +299,41 @@ export class EthWrappers extends React.Component + {_.isUndefined(etherscanUrl) ? + tokenLabel : +
+ {tokenLabel} + + } + + ); + } + private renderToken(name: string, address: string, imgPath: string) { + const tooltipId = `tooltip-${address}`; + return ( +
+ +
+ + {name} + + {address} +
+
+ ); + } private async onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) { this.setState({ outdatedWETHAddressToIsStateLoaded: { From b2256679bef6c39ee5889f12838d1c2e9d4dd832 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 15:41:24 -0500 Subject: [PATCH 031/125] Add `Max` convenience button in unwrap WETH dialog --- .../dialogs/eth_weth_conversion_dialog.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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 c8bdced9bd..230ac51839 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -109,7 +109,16 @@ export class EthWethConversionDialog extends className="pt1" style={{fontSize: 12}} > - 1 ETH = 1 WETH +
1 ETH = 1 WETH
+ {this.props.direction === Side.receive && +
+ Max +
+ }
@@ -137,6 +146,11 @@ export class EthWethConversionDialog extends ); } + private onMaxClick() { + this.setState({ + value: this.props.tokenState.balance, + }); + } private onValueChange(isValid: boolean, amount?: BigNumber) { this.setState({ value: amount, From 95dfac6f9bbf8cf0de2038cfbd4eabb53b979fcd Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 17:09:16 -0500 Subject: [PATCH 032/125] Make it such that Wrapped Ether page works on networks without any outdated WETH tokens --- .../website/ts/components/eth_wrappers.tsx | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index a6a1df7511..3ffb05ce02 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -246,8 +246,11 @@ export class EthWrappers extends React.Component { - const outdatedWETH = outdatedWETHByNetworkId[this.props.networkId]; - const timestampMsRange = outdatedWETH.timestampMsRange; + const outdatedWETHIfExists = outdatedWETHByNetworkId[this.props.networkId]; + if (_.isUndefined(outdatedWETHIfExists)) { + return null; // noop + } + const timestampMsRange = outdatedWETHIfExists.timestampMsRange; let dateRange: string; if (!_.isUndefined(timestampMsRange)) { const startMoment = moment(timestampMsRange.startTimestampMs); @@ -258,20 +261,20 @@ export class EthWrappers extends React.Component + {this.renderTokenLink(tokenLabel, etherscanUrl)} @@ -378,9 +381,14 @@ export class EthWrappers extends React.Component { - return outdatedWrappedEther[this.props.networkId].address; - }); + const outdatedWETHAddresses = _.compact(_.map(configs.outdatedWrappedEthers, outdatedWrappedEtherByNetwork => { + const outdatedWrappedEtherIfExists = outdatedWrappedEtherByNetwork[this.props.networkId]; + if (_.isUndefined(outdatedWrappedEtherIfExists)) { + return undefined + } + const address = outdatedWrappedEtherIfExists.address; + return address; + })); return outdatedWETHAddresses; } } // tslint:disable:max-file-line-count From 681617480ab8323cc81e1fc1e7a4f31a84b8b548 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 17:10:51 -0500 Subject: [PATCH 033/125] Move weth.io url to constants --- packages/website/ts/components/eth_wrappers.tsx | 2 +- packages/website/ts/utils/constants.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 3ffb05ce02..1a223a05ed 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -99,7 +99,7 @@ export class EthWrappers extends React.Component
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index fb81dd9a9a..8f645c770c 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -83,6 +83,7 @@ export const constants = { WEB3_DECODED_LOG_ENTRY_EVENT_URL: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L123', WEB3_LOG_ENTRY_EVENT_URL: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127', + WETH_IO_URL: 'https://weth.io/', ZEROEX_CHAT_URL: 'https://chat.0xproject.com', // Projects ETHFINEX_URL: 'https://www.bitfinex.com/ethfinex', From 672c8acaca76851306276d0d03c5df0c75aa8bc6 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 17:21:33 -0500 Subject: [PATCH 034/125] Fix linter errors --- packages/website/ts/components/eth_wrappers.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 1a223a05ed..a10313597d 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -268,7 +268,9 @@ export class EthWrappers extends React.Component { const outdatedWrappedEtherIfExists = outdatedWrappedEtherByNetwork[this.props.networkId]; if (_.isUndefined(outdatedWrappedEtherIfExists)) { - return undefined + return undefined; } const address = outdatedWrappedEtherIfExists.address; return address; From 89f368a8b8b2419f9f347ad20da94199d533fa26 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 18:58:12 -0500 Subject: [PATCH 035/125] Add notice dialog to balances page about updating the WETH contract. We also draw attention to the new dedicated section for unwrapping from outdated WETH tokens --- .../wrapped_eth_section_notice_dialog.tsx | 38 ++++++++++++++ packages/website/ts/components/portal.tsx | 50 +++++++++++++++---- packages/website/ts/utils/constants.ts | 1 + 3 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx diff --git a/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx new file mode 100644 index 0000000000..d13b2e8ce1 --- /dev/null +++ b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx @@ -0,0 +1,38 @@ +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import {colors} from 'material-ui/styles'; +import * as React from 'react'; + +interface WrappedEthSectionNoticeDialogProps { + isOpen: boolean; + onToggleDialog: () => void; +} + +export function WrappedEthSectionNoticeDialog(props: WrappedEthSectionNoticeDialogProps) { + return ( + , + ]} + open={props.isOpen} + onRequestClose={props.onToggleDialog.bind(this)} + autoScrollBodyContent={true} + modal={true} + > +
+
+ We have recently updated the Wrapped Ether token used by 0x Portal. + Don't worry, unwrapping Ether tied to the old Wrapped Ether token can + be done at any time by clicking on the "Wrap ETH" section in the menu + to the left. +
+
+
+ ); +} diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index 74b1bdec68..69a7c1e7f0 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -8,6 +8,7 @@ import {Route, Switch} from 'react-router-dom'; import {Blockchain} from 'ts/blockchain'; import {BlockchainErrDialog} from 'ts/components/dialogs/blockchain_err_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'; import {FillOrder} from 'ts/components/fill_order'; import {Footer} from 'ts/components/footer'; @@ -63,22 +64,39 @@ interface PortalAllState { prevNetworkId: number; prevNodeVersion: string; prevUserAddress: string; - hasAcceptedDisclaimer: boolean; + prevPathname: string; + isDisclaimerDialogOpen: boolean; + isWethNoticeDialogOpen: boolean; } export class Portal extends React.Component { private blockchain: Blockchain; private sharedOrderIfExists: Order; private throttledScreenWidthUpdate: () => void; + public static hasAlreadyDismissedWethNotice() { + const didDismissWethNotice = localStorage.getItemIfExists(constants.DISMISS_WETH_NOTICE_LOCAL_STORAGE_KEY); + const hasAlreadyDismissedWethNotice = !_.isUndefined(didDismissWethNotice) && + !_.isEmpty(didDismissWethNotice); + return hasAlreadyDismissedWethNotice; + } constructor(props: PortalAllProps) { super(props); this.sharedOrderIfExists = this.getSharedOrderIfExists(); this.throttledScreenWidthUpdate = _.throttle(this.updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + + const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`); + const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice(); + + const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.ACCEPT_DISCLAIMER_LOCAL_STORAGE_KEY); + const hasAcceptedDisclaimer = !_.isUndefined(didAcceptPortalDisclaimer) && + !_.isEmpty(didAcceptPortalDisclaimer); this.state = { prevNetworkId: this.props.networkId, prevNodeVersion: this.props.nodeVersion, prevUserAddress: this.props.userAddress, - hasAcceptedDisclaimer: false, + prevPathname: this.props.location.pathname, + isDisclaimerDialogOpen: !hasAcceptedDisclaimer, + isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, }; } public componentDidMount() { @@ -87,12 +105,6 @@ export class Portal extends React.Component { } public componentWillMount() { this.blockchain = new Blockchain(this.props.dispatcher); - const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.ACCEPT_DISCLAIMER_LOCAL_STORAGE_KEY); - const hasAcceptedDisclaimer = !_.isUndefined(didAcceptPortalDisclaimer) && - !_.isEmpty(didAcceptPortalDisclaimer); - this.setState({ - hasAcceptedDisclaimer, - }); } public componentWillUnmount() { this.blockchain.destroy(); @@ -128,6 +140,14 @@ export class Portal extends React.Component { // tslint:disable-next-line:no-floating-promises this.blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); } + if (nextProps.location.pathname !== this.state.prevPathname) { + const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`); + const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice(); + this.setState({ + prevPathname: nextProps.location.pathname, + isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, + }); + } } public render() { const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher @@ -215,8 +235,12 @@ export class Portal extends React.Component { toggleDialogFn={updateShouldBlockchainErrDialogBeOpen} networkId={this.props.networkId} /> + { private onPortalDisclaimerAccepted() { localStorage.setItem(constants.ACCEPT_DISCLAIMER_LOCAL_STORAGE_KEY, 'set'); this.setState({ - hasAcceptedDisclaimer: true, + isDisclaimerDialogOpen: false, + }); + } + private onWethNoticeAccepted() { + localStorage.setItem(constants.DISMISS_WETH_NOTICE_LOCAL_STORAGE_KEY, 'set'); + this.setState({ + isWethNoticeDialogOpen: false, }); } private getSharedOrderIfExists(): Order { diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 8f645c770c..cb2dbef884 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -34,6 +34,7 @@ export const constants = { GITHUB_URL: 'https://github.com/0xProject', GITHUB_WIKI_URL: 'https://github.com/0xProject/wiki', HTTP_NO_CONTENT_STATUS_CODE: 204, + DISMISS_WETH_NOTICE_LOCAL_STORAGE_KEY: 'hasDismissedWethNotice', ACCEPT_DISCLAIMER_LOCAL_STORAGE_KEY: 'didAcceptPortalDisclaimer', LINKEDIN_0X_URL: 'https://www.linkedin.com/company/0x', LEDGER_PROVIDER_NAME: 'Ledger', From 951fbc9b76faf6b0f9f44fd2b729bcb54cf9d565 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 19:18:33 -0500 Subject: [PATCH 036/125] Temporarily pretend as if new WETH contracts are already whitelisted by tokenRegistry and put hacks behind the shouldDeprecateOldWethToken flag --- packages/website/ts/blockchain.ts | 17 +++++++++++------ packages/website/ts/utils/configs.ts | 7 +++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index a42b19cffe..7a0d546b4d 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -158,6 +158,12 @@ export class Blockchain { } public async isAddressInTokenRegistryAsync(tokenAddress: string): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); + // HACK: temporarily whitelist the new WETH token address `as if` they were + // already in the tokenRegistry. + if (configs.shouldDeprecateOldWethToken && + tokenAddress === configs.newWrappedEthers[this.networkId]) { + return true; + } const tokenIfExists = await this.zeroEx.tokenRegistry.getTokenIfExistsAsync(tokenAddress); return !_.isUndefined(tokenIfExists); } @@ -598,12 +604,11 @@ export class Blockchain { // new canonical WETH. // TODO: Remove this hack once we've updated the TokenRegistries let address = t.address; - if (t.symbol === 'WETH') { - if (this.networkId === 1) { - address = '0xe495bcacaf29a0eb00fb67b86e9cd2a994dd55d8'; - } else if (this.networkId === 42) { - address = '0x739e78d6bebbdf24105a5145fa04436589d1cbd9'; - } + if (configs.shouldDeprecateOldWethToken && t.symbol === 'WETH') { + const newEtherTokenAddressIfExists = configs.newWrappedEthers[this.networkId]; + if (!_.isUndefined(newEtherTokenAddressIfExists)) { + address = newEtherTokenAddressIfExists; + } } const token: Token = { iconUrl, diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index 4a08929cf7..20efc66bed 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -19,6 +19,13 @@ export const configs = { lastLocalStorageFillClearanceDate: '2017-11-22', lastLocalStorageTrackedTokenClearanceDate: '2017-12-13', isMainnetEnabled: true, + shouldDeprecateOldWethToken: true, + // newWrappedEthers is temporary until we remove the shouldDeprecateOldWethToken flag + // and add the new WETHs to the tokenRegistry + newWrappedEthers: { + 1: '0xe495bcacaf29a0eb00fb67b86e9cd2a994dd55d8', + 42: '0x739e78d6bebbdf24105a5145fa04436589d1cbd9', + } as {[networkId: string]: string}, outdatedWrappedEthers: [ { 42: { From 22c4ee6ef709610155191c4b0629ba0c41ce908f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 19:35:24 -0500 Subject: [PATCH 037/125] Update first two string enums to native type --- packages/website/ts/blockchain.ts | 8 +++--- .../dialogs/blockchain_err_dialog.tsx | 8 +++--- .../dialogs/eth_weth_conversion_dialog.tsx | 8 +++--- .../components/eth_weth_conversion_button.tsx | 6 ++--- .../website/ts/components/eth_wrappers.tsx | 6 ++--- .../website/ts/components/fill_order_json.tsx | 4 +-- .../generate_order/generate_order_form.tsx | 26 +++++++++---------- .../ts/components/inputs/token_input.tsx | 2 +- .../website/ts/components/token_balances.tsx | 2 +- packages/website/ts/containers/portal.tsx | 4 +-- packages/website/ts/redux/reducer.ts | 10 +++---- packages/website/ts/types.ts | 22 +++++++--------- packages/website/ts/utils/utils.ts | 8 +++--- 13 files changed, 56 insertions(+), 58 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 6877a301a8..336397f7f5 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -135,11 +135,11 @@ export class Blockchain { const isConnected = !_.isUndefined(newNetworkId); if (!isConnected) { this.networkId = newNetworkId; - this.dispatcher.encounteredBlockchainError(BlockchainErrs.DISCONNECTED_FROM_ETHEREUM_NODE); + this.dispatcher.encounteredBlockchainError(BlockchainErrs.DisconnectedFromEthereumNode); this.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); } else if (this.networkId !== newNetworkId) { this.networkId = newNetworkId; - this.dispatcher.encounteredBlockchainError(''); + this.dispatcher.encounteredBlockchainError(BlockchainErrs.NoError); await this.fetchTokenInformationAsync(); await this.rehydrateStoreWithContractEvents(); } @@ -712,8 +712,8 @@ export class Blockchain { _.find(allTokens, {symbol: configs.defaultTrackedTokenSymbols[0]}), _.find(allTokens, {symbol: configs.defaultTrackedTokenSymbols[1]}), ]; - this.dispatcher.updateChosenAssetTokenAddress(Side.deposit, mostPopularTradingPairTokens[0].address); - this.dispatcher.updateChosenAssetTokenAddress(Side.receive, mostPopularTradingPairTokens[1].address); + 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 { diff --git a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx index 963bd43889..afc89be1ae 100644 --- a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx +++ b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx @@ -46,22 +46,22 @@ export class BlockchainErrDialog extends React.Component, ]; - const title = this.props.direction === Side.deposit ? 'Wrap ETH' : 'Unwrap WETH'; + const title = this.props.direction === Side.Deposit ? 'Wrap ETH' : 'Unwrap WETH'; return (
@@ -85,7 +85,7 @@ export class EthWethConversionDialog extends className="pt2 mx-auto" style={{width: 245}} > - {this.props.direction === Side.receive ? + {this.props.direction === Side.Receive ? { - const receiveAssetToken = state.sideToAssetToken[Side.receive]; - const depositAssetToken = state.sideToAssetToken[Side.deposit]; + const receiveAssetToken = state.sideToAssetToken[Side.Receive]; + const depositAssetToken = state.sideToAssetToken[Side.Deposit]; const receiveAddress = !_.isUndefined(receiveAssetToken.address) ? receiveAssetToken.address : constants.NULL_ADDRESS; const depositAddress = !_.isUndefined(depositAssetToken.address) ? diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index da69a9d006..9bd8899ada 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -58,7 +58,7 @@ export interface State { const INITIAL_STATE: State = { // Portal - blockchainErr: '', + blockchainErr: BlockchainErrs.NoError, blockchainIsLoaded: false, generateOrderStep: GenerateOrderSteps.ChooseAssets, networkId: undefined, @@ -76,8 +76,8 @@ const INITIAL_STATE: State = { screenWidth: utils.getScreenWidth(), shouldBlockchainErrDialogBeOpen: false, sideToAssetToken: { - [Side.deposit]: {}, - [Side.receive]: {}, + [Side.Deposit]: {}, + [Side.Receive]: {}, }, tokenByAddress: {}, tokenStateByAddress: {}, @@ -294,8 +294,8 @@ export function reducer(state: State = INITIAL_STATE, action: Action) { case ActionTypes.SWAP_ASSET_TOKENS: { const newSideToAssetToken = _.assign({}, state.sideToAssetToken, { - [Side.deposit]: state.sideToAssetToken[Side.receive], - [Side.receive]: state.sideToAssetToken[Side.deposit], + [Side.Deposit]: state.sideToAssetToken[Side.Receive], + [Side.Receive]: state.sideToAssetToken[Side.Deposit], }); return _.assign({}, state, { sideToAssetToken: newSideToAssetToken, diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 2005cf2655..085b467892 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -18,18 +18,10 @@ export enum GenerateOrderSteps { CopyAndShare, } -export const Side = strEnum([ - 'receive', - 'deposit', -]); -export type Side = keyof typeof Side; - -export const BlockchainErrs = strEnum([ - 'A_CONTRACT_NOT_DEPLOYED_ON_NETWORK', - 'DISCONNECTED_FROM_ETHEREUM_NODE', - 'UNHANDLED_ERROR', -]); -export type BlockchainErrs = keyof typeof BlockchainErrs; +export enum Side { + Receive = 'RECEIVE', + Deposit = 'DEPOSIT', +} export const Direction = strEnum([ 'forward', @@ -271,6 +263,12 @@ export const EtherscanLinkSuffixes = strEnum([ ]); export type EtherscanLinkSuffixes = keyof typeof EtherscanLinkSuffixes; +export enum BlockchainErrs { + AContractNotDeployedOnNetwork = 'A_CONTRACT_NOT_DEPLOYED_ON_NETWORK', + DisconnectedFromEthereumNode = 'DISCONNECTED_FROM_ETHEREUM_NODE', + NoError = 'NO_ERROR', +} + export const BlockchainCallErrs = strEnum([ 'CONTRACT_DOES_NOT_EXIST', 'USER_HAS_NO_ASSOCIATED_ADDRESSES', diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index abc2fb2d4d..a6f80c2ab5 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -61,8 +61,8 @@ export const utils = { orderExpiryTimestamp: BigNumber, orderTakerAddress: string, orderMakerAddress: string, makerFee: BigNumber, takerFee: BigNumber, feeRecipient: string, signatureData: SignatureData, tokenByAddress: TokenByAddress, orderSalt: BigNumber): Order { - const makerToken = tokenByAddress[sideToAssetToken[Side.deposit].address]; - const takerToken = tokenByAddress[sideToAssetToken[Side.receive].address]; + const makerToken = tokenByAddress[sideToAssetToken[Side.Deposit].address]; + const takerToken = tokenByAddress[sideToAssetToken[Side.Receive].address]; const order = { maker: { address: orderMakerAddress, @@ -72,7 +72,7 @@ export const utils = { decimals: makerToken.decimals, address: makerToken.address, }, - amount: sideToAssetToken[Side.deposit].amount.toString(), + amount: sideToAssetToken[Side.Deposit].amount.toString(), feeAmount: makerFee.toString(), }, taker: { @@ -83,7 +83,7 @@ export const utils = { decimals: takerToken.decimals, address: takerToken.address, }, - amount: sideToAssetToken[Side.receive].amount.toString(), + amount: sideToAssetToken[Side.Receive].amount.toString(), feeAmount: takerFee.toString(), }, expiration: orderExpiryTimestamp.toString(), From 449fd9f0242b031c7427ae9a95430008c3171d1c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 19:38:59 -0500 Subject: [PATCH 038/125] Remove unused enums --- packages/website/ts/redux/dispatcher.ts | 7 ------- packages/website/ts/redux/reducer.ts | 17 ----------------- packages/website/ts/types.ts | 15 --------------- 3 files changed, 39 deletions(-) diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index cbcee65993..e37e1fa6c4 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -5,7 +5,6 @@ import { ActionTypes, AssetToken, BlockchainErrs, - Direction, Order, ProviderType, ScreenWidths, @@ -43,12 +42,6 @@ export class Dispatcher { type: ActionTypes.SWAP_ASSET_TOKENS, }); } - public updateGenerateOrderStep(direction: Direction) { - this.dispatch({ - data: direction, - type: ActionTypes.UPDATE_GENERATE_ORDER_STEP, - }); - } public updateOrderSalt(salt: BigNumber) { this.dispatch({ data: salt, diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 9bd8899ada..361e5f8c67 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -5,8 +5,6 @@ import { Action, ActionTypes, BlockchainErrs, - Direction, - GenerateOrderSteps, Order, ProviderType, ScreenWidths, @@ -28,7 +26,6 @@ export interface State { // Portal blockchainErr: BlockchainErrs; blockchainIsLoaded: boolean; - generateOrderStep: GenerateOrderSteps; networkId: number; orderExpiryTimestamp: BigNumber; orderFillAmount: BigNumber; @@ -60,7 +57,6 @@ const INITIAL_STATE: State = { // Portal blockchainErr: BlockchainErrs.NoError, blockchainIsLoaded: false, - generateOrderStep: GenerateOrderSteps.ChooseAssets, networkId: undefined, orderExpiryTimestamp: utils.initialOrderExpiryUnixTimestampSec(), orderFillAmount: undefined, @@ -259,19 +255,6 @@ export function reducer(state: State = INITIAL_STATE, action: Action) { }); } - case ActionTypes.UPDATE_GENERATE_ORDER_STEP: { - const direction = action.data; - let nextGenerateOrderStep = state.generateOrderStep; - if (direction === Direction.forward) { - nextGenerateOrderStep += 1; - } else if (state.generateOrderStep !== 0) { - nextGenerateOrderStep -= 1; - } - return _.assign({}, state, { - generateOrderStep: nextGenerateOrderStep, - }); - } - case ActionTypes.UPDATE_CHOSEN_ASSET_TOKEN: { const newSideToAssetToken = _.assign({}, state.sideToAssetToken, { [action.data.side]: action.data.token, diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 085b467892..52d30ae351 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -10,25 +10,11 @@ function strEnum(values: string[]): {[key: string]: string} { }, Object.create(null)); } -export enum GenerateOrderSteps { - ChooseAssets, - GrantAllowance, - RemainingConfigs, - SignTransaction, - CopyAndShare, -} - export enum Side { Receive = 'RECEIVE', Deposit = 'DEPOSIT', } -export const Direction = strEnum([ - 'forward', - 'backward', -]); -export type Direction = keyof typeof Direction; - export interface Token { iconUrl?: string; name: string; @@ -141,7 +127,6 @@ export const ActionTypes = strEnum([ 'CLEAR_TOKEN_BY_ADDRESS', 'UPDATE_BLOCKCHAIN_IS_LOADED', 'UPDATE_NETWORK_ID', - 'UPDATE_GENERATE_ORDER_STEP', 'UPDATE_CHOSEN_ASSET_TOKEN', 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS', 'UPDATE_ORDER_TAKER_ADDRESS', From 90d274ffc4cce36c1ff572fad10bd7896e255ada Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 20:06:49 -0500 Subject: [PATCH 039/125] Update ActionType enum and move from using _.assign in reducer --- packages/website/ts/redux/dispatcher.ts | 66 +++--- packages/website/ts/redux/reducer.ts | 265 ++++++++++++++---------- packages/website/ts/types.ts | 71 ++++--- 3 files changed, 222 insertions(+), 180 deletions(-) diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index e37e1fa6c4..ad5553c58b 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -22,42 +22,42 @@ export class Dispatcher { // Portal public resetState() { this.dispatch({ - type: ActionTypes.RESET_STATE, + type: ActionTypes.ResetState, }); } public updateNodeVersion(nodeVersion: string) { this.dispatch({ data: nodeVersion, - type: ActionTypes.UPDATE_NODE_VERSION, + type: ActionTypes.UpdateNodeVersion, }); } public updateScreenWidth(screenWidth: ScreenWidths) { this.dispatch({ data: screenWidth, - type: ActionTypes.UPDATE_SCREEN_WIDTH, + type: ActionTypes.UpdateScreenWidth, }); } public swapAssetTokenSymbols() { this.dispatch({ - type: ActionTypes.SWAP_ASSET_TOKENS, + type: ActionTypes.SwapAssetTokens, }); } public updateOrderSalt(salt: BigNumber) { this.dispatch({ data: salt, - type: ActionTypes.UPDATE_ORDER_SALT, + type: ActionTypes.UpdateOrderSalt, }); } public updateUserSuppliedOrderCache(order: Order) { this.dispatch({ data: order, - type: ActionTypes.UPDATE_USER_SUPPLIED_ORDER_CACHE, + type: ActionTypes.UpdateUserSuppliedOrderCache, }); } public updateShouldBlockchainErrDialogBeOpen(shouldBeOpen: boolean) { this.dispatch({ data: shouldBeOpen, - type: ActionTypes.UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN, + type: ActionTypes.UpdateShouldBlockchainErrDialogBeOpen, }); } public updateChosenAssetToken(side: Side, token: AssetToken) { @@ -66,7 +66,7 @@ export class Dispatcher { side, token, }, - type: ActionTypes.UPDATE_CHOSEN_ASSET_TOKEN, + type: ActionTypes.UpdateChosenAssetToken, }); } public updateChosenAssetTokenAddress(side: Side, address: string) { @@ -75,72 +75,72 @@ export class Dispatcher { address, side, }, - type: ActionTypes.UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS, + type: ActionTypes.UpdateChosenAssetTokenAddress, }); } public updateOrderTakerAddress(address: string) { this.dispatch({ data: address, - type: ActionTypes.UPDATE_ORDER_TAKER_ADDRESS, + type: ActionTypes.UpdateOrderTakerAddress, }); } public updateUserAddress(address: string) { this.dispatch({ data: address, - type: ActionTypes.UPDATE_USER_ADDRESS, + type: ActionTypes.UpdateUserAddress, }); } public updateOrderExpiry(unixTimestampSec: BigNumber) { this.dispatch({ data: unixTimestampSec, - type: ActionTypes.UPDATE_ORDER_EXPIRY, + type: ActionTypes.UpdateOrderExpiry, }); } public encounteredBlockchainError(err: BlockchainErrs) { this.dispatch({ data: err, - type: ActionTypes.BLOCKCHAIN_ERR_ENCOUNTERED, + type: ActionTypes.BlockchainErrEncountered, }); } public updateBlockchainIsLoaded(isLoaded: boolean) { this.dispatch({ data: isLoaded, - type: ActionTypes.UPDATE_BLOCKCHAIN_IS_LOADED, + type: ActionTypes.UpdateBlockchainIsLoaded, }); } public addTokenToTokenByAddress(token: Token) { this.dispatch({ data: token, - type: ActionTypes.ADD_TOKEN_TO_TOKEN_BY_ADDRESS, + type: ActionTypes.AddTokenToTokenByAddress, }); } public removeTokenToTokenByAddress(token: Token) { this.dispatch({ data: token, - type: ActionTypes.REMOVE_TOKEN_TO_TOKEN_BY_ADDRESS, + type: ActionTypes.RemoveTokenFromTokenByAddress, }); } public clearTokenByAddress() { this.dispatch({ - type: ActionTypes.CLEAR_TOKEN_BY_ADDRESS, + type: ActionTypes.ClearTokenByAddress, }); } public updateTokenByAddress(tokens: Token[]) { this.dispatch({ data: tokens, - type: ActionTypes.UPDATE_TOKEN_BY_ADDRESS, + type: ActionTypes.UpdateTokenByAddress, }); } public updateTokenStateByAddress(tokenStateByAddress: TokenStateByAddress) { this.dispatch({ data: tokenStateByAddress, - type: ActionTypes.UPDATE_TOKEN_STATE_BY_ADDRESS, + type: ActionTypes.UpdateTokenStateByAddress, }); } public removeFromTokenStateByAddress(tokenAddress: string) { this.dispatch({ data: tokenAddress, - type: ActionTypes.REMOVE_FROM_TOKEN_STATE_BY_ADDRESS, + type: ActionTypes.RemoveFromTokenStateByAddress, }); } public replaceTokenAllowanceByAddress(address: string, allowance: BigNumber) { @@ -149,7 +149,7 @@ export class Dispatcher { address, allowance, }, - type: ActionTypes.REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS, + type: ActionTypes.ReplaceTokenAllowanceByAddress, }); } public replaceTokenBalanceByAddress(address: string, balance: BigNumber) { @@ -158,7 +158,7 @@ export class Dispatcher { address, balance, }, - type: ActionTypes.REPLACE_TOKEN_BALANCE_BY_ADDRESS, + type: ActionTypes.ReplaceTokenBalanceByAddress, }); } public updateTokenBalanceByAddress(address: string, balanceDelta: BigNumber) { @@ -167,31 +167,31 @@ export class Dispatcher { address, balanceDelta, }, - type: ActionTypes.UPDATE_TOKEN_BALANCE_BY_ADDRESS, + type: ActionTypes.UpdateTokenBalanceByAddress, }); } public updateSignatureData(signatureData: SignatureData) { this.dispatch({ data: signatureData, - type: ActionTypes.UPDATE_ORDER_SIGNATURE_DATA, + type: ActionTypes.UpdateOrderSignatureData, }); } public updateUserEtherBalance(balance: BigNumber) { this.dispatch({ data: balance, - type: ActionTypes.UPDATE_USER_ETHER_BALANCE, + type: ActionTypes.UpdateUserEtherBalance, }); } public updateNetworkId(networkId: number) { this.dispatch({ data: networkId, - type: ActionTypes.UPDATE_NETWORK_ID, + type: ActionTypes.UpdateNetworkId, }); } public updateOrderFillAmount(amount: BigNumber) { this.dispatch({ data: amount, - type: ActionTypes.UPDATE_ORDER_FILL_AMOUNT, + type: ActionTypes.UpdateOrderFillAmount, }); } @@ -199,13 +199,13 @@ export class Dispatcher { public updateCurrentDocsVersion(version: string) { this.dispatch({ data: version, - type: ActionTypes.UPDATE_LIBRARY_VERSION, + type: ActionTypes.UpdateLibraryVersion, }); } public updateAvailableDocVersions(versions: string[]) { this.dispatch({ data: versions, - type: ActionTypes.UPDATE_AVAILABLE_LIBRARY_VERSIONS, + type: ActionTypes.UpdateAvailableLibraryVersions, }); } @@ -213,23 +213,23 @@ export class Dispatcher { public showFlashMessage(msg: string|React.ReactNode) { this.dispatch({ data: msg, - type: ActionTypes.SHOW_FLASH_MESSAGE, + type: ActionTypes.ShowFlashMessage, }); } public hideFlashMessage() { this.dispatch({ - type: ActionTypes.HIDE_FLASH_MESSAGE, + type: ActionTypes.HideFlashMessage, }); } public updateProviderType(providerType: ProviderType) { this.dispatch({ - type: ActionTypes.UPDATE_PROVIDER_TYPE, + type: ActionTypes.UpdateProviderType, data: providerType, }); } public updateInjectedProviderName(injectedProviderName: string) { this.dispatch({ - type: ActionTypes.UPDATE_INJECTED_PROVIDER_NAME, + type: ActionTypes.UpdateInjectedProviderName, data: injectedProviderName, }); } diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 361e5f8c67..43bdeaa83a 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -94,250 +94,293 @@ const INITIAL_STATE: State = { export function reducer(state: State = INITIAL_STATE, action: Action) { switch (action.type) { // Portal - case ActionTypes.RESET_STATE: + case ActionTypes.ResetState: return INITIAL_STATE; - case ActionTypes.UPDATE_ORDER_SALT: { - return _.assign({}, state, { + case ActionTypes.UpdateOrderSalt: { + return { + ...state, orderSalt: action.data, - }); + }; } - case ActionTypes.UPDATE_NODE_VERSION: { - return _.assign({}, state, { + case ActionTypes.UpdateNodeVersion: { + return { + ...state, nodeVersion: action.data, - }); + }; } - case ActionTypes.UPDATE_ORDER_FILL_AMOUNT: { - return _.assign({}, state, { + case ActionTypes.UpdateOrderFillAmount: { + return { + ...state, orderFillAmount: action.data, - }); + }; } - case ActionTypes.UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN: { - return _.assign({}, state, { + case ActionTypes.UpdateShouldBlockchainErrDialogBeOpen: { + return { + ...state, shouldBlockchainErrDialogBeOpen: action.data, - }); + }; } - case ActionTypes.UPDATE_USER_ETHER_BALANCE: { - return _.assign({}, state, { + case ActionTypes.UpdateUserEtherBalance: { + return { + ...state, userEtherBalance: action.data, - }); + }; } - case ActionTypes.UPDATE_USER_SUPPLIED_ORDER_CACHE: { - return _.assign({}, state, { + case ActionTypes.UpdateUserSuppliedOrderCache: { + return { + ...state, userSuppliedOrderCache: action.data, - }); + }; } - case ActionTypes.CLEAR_TOKEN_BY_ADDRESS: { - return _.assign({}, state, { + case ActionTypes.ClearTokenByAddress: { + return { + ...state, tokenByAddress: {}, - }); + }; } - case ActionTypes.ADD_TOKEN_TO_TOKEN_BY_ADDRESS: { + case ActionTypes.AddTokenToTokenByAddress: { const newTokenByAddress = state.tokenByAddress; newTokenByAddress[action.data.address] = action.data; - return _.assign({}, state, { + return { + ...state, tokenByAddress: newTokenByAddress, - }); + }; } - case ActionTypes.REMOVE_TOKEN_TO_TOKEN_BY_ADDRESS: { + case ActionTypes.RemoveTokenFromTokenByAddress: { const newTokenByAddress = state.tokenByAddress; delete newTokenByAddress[action.data.address]; - return _.assign({}, state, { + return { + ...state, tokenByAddress: newTokenByAddress, - }); + }; } - case ActionTypes.UPDATE_TOKEN_BY_ADDRESS: { + case ActionTypes.UpdateTokenByAddress: { const tokenByAddress = state.tokenByAddress; const tokens = action.data; _.each(tokens, token => { - const updatedToken = _.assign({}, tokenByAddress[token.address], token); + const updatedToken = { + ...tokenByAddress[token.address], + ...token, + }; tokenByAddress[token.address] = updatedToken; }); - return _.assign({}, state, { + return { + ...state, tokenByAddress, - }); + }; } - case ActionTypes.UPDATE_TOKEN_STATE_BY_ADDRESS: { + case ActionTypes.UpdateTokenStateByAddress: { const tokenStateByAddress = state.tokenStateByAddress; const updatedTokenStateByAddress = action.data; _.each(updatedTokenStateByAddress, (tokenState: TokenState, address: string) => { - const updatedTokenState = _.assign({}, tokenStateByAddress[address], tokenState); + const updatedTokenState = { + ...tokenStateByAddress[address], + ...tokenState, + }; tokenStateByAddress[address] = updatedTokenState; }); - return _.assign({}, state, { + return { + ...state, tokenStateByAddress, - }); + }; } - case ActionTypes.REMOVE_FROM_TOKEN_STATE_BY_ADDRESS: { + case ActionTypes.RemoveFromTokenStateByAddress: { const tokenStateByAddress = state.tokenStateByAddress; const tokenAddress = action.data; delete tokenStateByAddress[tokenAddress]; - return _.assign({}, state, { + return { + ...state, tokenStateByAddress, - }); + }; } - case ActionTypes.REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS: { + case ActionTypes.ReplaceTokenAllowanceByAddress: { const tokenStateByAddress = state.tokenStateByAddress; const allowance = action.data.allowance; const tokenAddress = action.data.address; - tokenStateByAddress[tokenAddress] = _.assign({}, tokenStateByAddress[tokenAddress], { + tokenStateByAddress[tokenAddress] = { + ...tokenStateByAddress[tokenAddress], allowance, - }); - return _.assign({}, state, { + }; + return { + ...state, tokenStateByAddress, - }); + }; } - case ActionTypes.REPLACE_TOKEN_BALANCE_BY_ADDRESS: { + case ActionTypes.ReplaceTokenBalanceByAddress: { const tokenStateByAddress = state.tokenStateByAddress; const balance = action.data.balance; const tokenAddress = action.data.address; - tokenStateByAddress[tokenAddress] = _.assign({}, tokenStateByAddress[tokenAddress], { + tokenStateByAddress[tokenAddress] = { + ...tokenStateByAddress[tokenAddress], balance, - }); - return _.assign({}, state, { + }; + return { + ...state, tokenStateByAddress, - }); + }; } - case ActionTypes.UPDATE_TOKEN_BALANCE_BY_ADDRESS: { + case ActionTypes.UpdateTokenBalanceByAddress: { const tokenStateByAddress = state.tokenStateByAddress; const balanceDelta = action.data.balanceDelta; const tokenAddress = action.data.address; const currBalance = tokenStateByAddress[tokenAddress].balance; - tokenStateByAddress[tokenAddress] = _.assign({}, tokenStateByAddress[tokenAddress], { + tokenStateByAddress[tokenAddress] = { + ...tokenStateByAddress[tokenAddress], balance: currBalance.plus(balanceDelta), - }); - return _.assign({}, state, { + }; + return { + ...state, tokenStateByAddress, - }); + }; } - case ActionTypes.UPDATE_ORDER_SIGNATURE_DATA: { - return _.assign({}, state, { + case ActionTypes.UpdateOrderSignatureData: { + return { + ...state, orderSignatureData: action.data, - }); + }; } - case ActionTypes.UPDATE_SCREEN_WIDTH: { - return _.assign({}, state, { + case ActionTypes.UpdateScreenWidth: { + return { + ...state, screenWidth: action.data, - }); + }; } - case ActionTypes.UPDATE_BLOCKCHAIN_IS_LOADED: { - return _.assign({}, state, { + case ActionTypes.UpdateBlockchainIsLoaded: { + return { + ...state, blockchainIsLoaded: action.data, - }); + }; } - case ActionTypes.BLOCKCHAIN_ERR_ENCOUNTERED: { - return _.assign({}, state, { + case ActionTypes.BlockchainErrEncountered: { + return { + ...state, blockchainErr: action.data, - }); + }; } - case ActionTypes.UPDATE_NETWORK_ID: { - return _.assign({}, state, { + case ActionTypes.UpdateNetworkId: { + return { + ...state, networkId: action.data, - }); + }; } - case ActionTypes.UPDATE_CHOSEN_ASSET_TOKEN: { - const newSideToAssetToken = _.assign({}, state.sideToAssetToken, { + case ActionTypes.UpdateChosenAssetToken: { + const newSideToAssetToken = { + ...state.sideToAssetToken, [action.data.side]: action.data.token, - }); - return _.assign({}, state, { + }; + return { + ...state, sideToAssetToken: newSideToAssetToken, - }); + }; } - case ActionTypes.UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS: { + case ActionTypes.UpdateChosenAssetTokenAddress: { const newAssetToken = state.sideToAssetToken[action.data.side]; newAssetToken.address = action.data.address; - const newSideToAssetToken = _.assign({}, state.sideToAssetToken, { + const newSideToAssetToken = { + ...state.sideToAssetToken, [action.data.side]: newAssetToken, - }); - return _.assign({}, state, { + }; + return { + ...state, sideToAssetToken: newSideToAssetToken, - }); + }; } - case ActionTypes.SWAP_ASSET_TOKENS: { - const newSideToAssetToken = _.assign({}, state.sideToAssetToken, { + case ActionTypes.SwapAssetTokens: { + const newSideToAssetToken = { [Side.Deposit]: state.sideToAssetToken[Side.Receive], [Side.Receive]: state.sideToAssetToken[Side.Deposit], - }); - return _.assign({}, state, { + }; + return { + ...state, sideToAssetToken: newSideToAssetToken, - }); + }; } - case ActionTypes.UPDATE_ORDER_EXPIRY: { - return _.assign({}, state, { + case ActionTypes.UpdateOrderExpiry: { + return { + ...state, orderExpiryTimestamp: action.data, - }); + }; } - case ActionTypes.UPDATE_ORDER_TAKER_ADDRESS: { - return _.assign({}, state, { + case ActionTypes.UpdateOrderTakerAddress: { + return { + ...state, orderTakerAddress: action.data, - }); + }; } - case ActionTypes.UPDATE_USER_ADDRESS: { - return _.assign({}, state, { + case ActionTypes.UpdateUserAddress: { + return { + ...state, userAddress: action.data, - }); + }; } // Docs - case ActionTypes.UPDATE_LIBRARY_VERSION: { - return _.assign({}, state, { + case ActionTypes.UpdateLibraryVersion: { + return { + ...state, docsVersion: action.data, - }); + }; } - case ActionTypes.UPDATE_AVAILABLE_LIBRARY_VERSIONS: { - return _.assign({}, state, { + case ActionTypes.UpdateAvailableLibraryVersions: { + return { + ...state, availableDocVersions: action.data, - }); + }; } // Shared - case ActionTypes.SHOW_FLASH_MESSAGE: { - return _.assign({}, state, { + case ActionTypes.ShowFlashMessage: { + return { + ...state, flashMessage: action.data, - }); + }; } - case ActionTypes.HIDE_FLASH_MESSAGE: { - return _.assign({}, state, { + case ActionTypes.HideFlashMessage: { + return { + ...state, flashMessage: undefined, - }); + }; } - case ActionTypes.UPDATE_PROVIDER_TYPE: { - return _.assign({}, state, { + case ActionTypes.UpdateProviderType: { + return { + ...state, providerType: action.data, - }); + }; } - case ActionTypes.UPDATE_INJECTED_PROVIDER_NAME: { - return _.assign({}, state, { + case ActionTypes.UpdateInjectedProviderName: { + return { + ...state, injectedProviderName: action.data, - }); + }; } default: diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 52d30ae351..58f44dd458 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -117,47 +117,46 @@ export enum BalanceErrs { allowanceSettingFailed, } -export const ActionTypes = strEnum([ +export enum ActionTypes { // Portal - 'UPDATE_SCREEN_WIDTH', - 'UPDATE_NODE_VERSION', - 'RESET_STATE', - 'ADD_TOKEN_TO_TOKEN_BY_ADDRESS', - 'BLOCKCHAIN_ERR_ENCOUNTERED', - 'CLEAR_TOKEN_BY_ADDRESS', - 'UPDATE_BLOCKCHAIN_IS_LOADED', - 'UPDATE_NETWORK_ID', - 'UPDATE_CHOSEN_ASSET_TOKEN', - 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS', - 'UPDATE_ORDER_TAKER_ADDRESS', - 'UPDATE_ORDER_SALT', - 'UPDATE_ORDER_SIGNATURE_DATA', - 'UPDATE_TOKEN_BY_ADDRESS', - 'REMOVE_TOKEN_TO_TOKEN_BY_ADDRESS', - 'UPDATE_TOKEN_STATE_BY_ADDRESS', - 'REMOVE_FROM_TOKEN_STATE_BY_ADDRESS', - 'REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS', - 'REPLACE_TOKEN_BALANCE_BY_ADDRESS', - 'UPDATE_TOKEN_BALANCE_BY_ADDRESS', - 'UPDATE_ORDER_EXPIRY', - 'SWAP_ASSET_TOKENS', - 'UPDATE_USER_ADDRESS', - 'UPDATE_USER_ETHER_BALANCE', - 'UPDATE_USER_SUPPLIED_ORDER_CACHE', - 'UPDATE_ORDER_FILL_AMOUNT', - 'UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN', + UpdateScreenWidth = 'UPDATE_SCREEN_WIDTH', + UpdateNodeVersion = 'UPDATE_NODE_VERSION', + 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', + UpdateChosenAssetTokenAddress = 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS', + UpdateOrderTakerAddress = 'UPDATE_ORDER_TAKER_ADDRESS', + UpdateOrderSalt = 'UPDATE_ORDER_SALT', + 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', + UpdateOrderExpiry = 'UPDATE_ORDER_EXPIRY', + SwapAssetTokens = 'SWAP_ASSET_TOKENS', + UpdateUserAddress = 'UPDATE_USER_ADDRESS', + UpdateUserEtherBalance = 'UPDATE_USER_ETHER_BALANCE', + UpdateUserSuppliedOrderCache = 'UPDATE_USER_SUPPLIED_ORDER_CACHE', + UpdateOrderFillAmount = 'UPDATE_ORDER_FILL_AMOUNT', + UpdateShouldBlockchainErrDialogBeOpen = 'UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN', // Docs - 'UPDATE_LIBRARY_VERSION', - 'UPDATE_AVAILABLE_LIBRARY_VERSIONS', + UpdateLibraryVersion = 'UPDATE_LIBRARY_VERSION', + UpdateAvailableLibraryVersions = 'UPDATE_AVAILABLE_LIBRARY_VERSIONS', // Shared - 'SHOW_FLASH_MESSAGE', - 'HIDE_FLASH_MESSAGE', - 'UPDATE_PROVIDER_TYPE', - 'UPDATE_INJECTED_PROVIDER_NAME', -]); -export type ActionTypes = keyof typeof ActionTypes; + ShowFlashMessage = 'SHOW_FLASH_MESSAGE', + HideFlashMessage = 'HIDE_FLASH_MESSAGE', + UpdateProviderType = 'UPDATE_PROVIDER_TYPE', + UpdateInjectedProviderName = 'UPDATE_INJECTED_PROVIDER_NAME', +} export interface Action { type: ActionTypes; From abbad68eb838ec4611d2236a7b0036a4a4d3058e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 20:48:23 -0500 Subject: [PATCH 040/125] Replace remaining strEnums with property TS string enums --- packages/website/ts/blockchain.ts | 30 ++++---- .../components/eth_weth_conversion_button.tsx | 2 +- .../website/ts/components/send_button.tsx | 2 +- .../website/ts/components/token_balances.tsx | 12 +-- .../trade_history/trade_history_item.tsx | 2 +- .../ts/components/ui/ethereum_address.tsx | 2 +- .../ts/components/ui/etherscan_icon.tsx | 2 +- packages/website/ts/components/ui/party.tsx | 2 +- .../ts/pages/documentation/documentation.tsx | 2 +- .../pages/documentation/type_definition.tsx | 2 +- packages/website/ts/pages/landing/landing.tsx | 24 +++--- packages/website/ts/redux/reducer.ts | 2 +- packages/website/ts/types.ts | 75 ++++++++----------- packages/website/ts/utils/typedoc_utils.ts | 4 +- packages/website/ts/utils/utils.ts | 6 +- 15 files changed, 78 insertions(+), 91 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 336397f7f5..c8ebd6b938 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -186,7 +186,7 @@ export class Blockchain { // later on in the logic. let provider; switch (providerType) { - case ProviderType.LEDGER: { + case ProviderType.Ledger: { const isU2FSupported = await utils.isU2FSupportedAsync(); if (!isU2FSupported) { throw new Error('Cannot update providerType to LEDGER without U2F support'); @@ -220,7 +220,7 @@ export class Blockchain { break; } - case ProviderType.INJECTED: { + case ProviderType.Injected: { if (_.isUndefined(this.cachedProvider)) { return; // Going from injected to injected, so we noop } @@ -241,8 +241,8 @@ export class Blockchain { await this.fetchTokenInformationAsync(); } public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { - utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TOKEN_ADDRESS_IS_INVALID); - utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); + utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid); + utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); const txHash = await this.zeroEx.token.setProxyAllowanceAsync( @@ -258,7 +258,7 @@ export class Blockchain { token.address, this.userAddress, toAddress, amountInBaseUnits, ); await this.showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.tx); + const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx); this.dispatcher.showFlashMessage(React.createElement(TokenSendCompleted, { etherScanLinkIfExists, token, @@ -294,7 +294,7 @@ export class Blockchain { } public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise { - utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); + utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); const shouldThrowOnInsufficientBalanceOrAllowance = true; @@ -346,7 +346,7 @@ export class Blockchain { return this.web3Wrapper.isAddress(lowercaseAddress); } public async pollTokenBalanceAsync(token: Token) { - utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); + utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this.userAddress, token.address); @@ -375,7 +375,7 @@ export class Blockchain { return signatureData; } public async mintTestTokensAsync(token: Token) { - utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); + utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); const mintableContract = await this.instantiateContractIfExistsAsync(MintableArtifacts, token.address); await mintableContract.mint(constants.MINT_AMOUNT, { @@ -390,14 +390,14 @@ export class Blockchain { } public async convertEthToWrappedEthTokensAsync(amount: BigNumber): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); - utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); + utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); const txHash = await this.zeroEx.etherToken.depositAsync(amount, this.userAddress); await this.showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } public async convertWrappedEthTokensToEthAsync(amount: BigNumber): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); - utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); + utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); const txHash = await this.zeroEx.etherToken.withdrawAsync(amount, this.userAddress); await this.showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); @@ -463,7 +463,7 @@ export class Blockchain { } private async showEtherScanLinkAndAwaitTransactionMinedAsync( txHash: string): Promise { - const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.tx); + const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx); this.dispatcher.showFlashMessage(React.createElement(TransactionSubmitted, { etherScanLinkIfExists, })); @@ -491,7 +491,7 @@ export class Blockchain { } private async startListeningForExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues): Promise { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); - utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES); + utils.assert(this.doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); // Fetch historical logs await this.fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues); @@ -733,7 +733,7 @@ export class Blockchain { const doesContractExist = await this.doesContractExistAtAddressAsync(contractAddress); if (!doesContractExist) { utils.consoleLog(`Contract does not exist: ${artifact.contract_name} at ${contractAddress}`); - throw new Error(BlockchainCallErrs.CONTRACT_DOES_NOT_EXIST); + throw new Error(BlockchainCallErrs.ContractDoesNotExist); } } @@ -746,10 +746,10 @@ export class Blockchain { const errMsg = `${err}`; utils.consoleLog(`Notice: Error encountered: ${err} ${err.stack}`); if (_.includes(errMsg, 'not been deployed to detected network')) { - throw new Error(BlockchainCallErrs.CONTRACT_DOES_NOT_EXIST); + throw new Error(BlockchainCallErrs.ContractDoesNotExist); } else { await errorReporter.reportAsync(err); - throw new Error(BlockchainCallErrs.UNHANDLED_ERROR); + throw new Error(BlockchainCallErrs.UnhandledError); } } } diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index ae919921d3..5464f8df0f 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -104,7 +104,7 @@ export class EthWethConversionButton extends this.props.onConversionSuccessful(); } catch (err) { const errMsg = '' + err; - if (_.includes(errMsg, BlockchainCallErrs.USER_HAS_NO_ASSOCIATED_ADDRESSES)) { + if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); } else if (!_.includes(errMsg, 'User denied transaction')) { utils.consoleLog(`Unexpected error encountered: ${err}`); diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx index b3fd2aebae..1fc3009648 100644 --- a/packages/website/ts/components/send_button.tsx +++ b/packages/website/ts/components/send_button.tsx @@ -70,7 +70,7 @@ export class SendButton extends React.Component } - {this.props.screenWidth !== ScreenWidths.SM && + {this.props.screenWidth !== ScreenWidths.Sm && @@ -580,7 +580,7 @@ export class TokenBalances extends React.Component
diff --git a/packages/website/ts/components/ui/ethereum_address.tsx b/packages/website/ts/components/ui/ethereum_address.tsx index b3bc0bc592..d568406895 100644 --- a/packages/website/ts/components/ui/ethereum_address.tsx +++ b/packages/website/ts/components/ui/ethereum_address.tsx @@ -26,7 +26,7 @@ export const EthereumAddress = (props: EthereumAddressProps) => { {props.address} diff --git a/packages/website/ts/components/ui/etherscan_icon.tsx b/packages/website/ts/components/ui/etherscan_icon.tsx index 9b4d172f12..3ccbf0bd9f 100644 --- a/packages/website/ts/components/ui/etherscan_icon.tsx +++ b/packages/website/ts/components/ui/etherscan_icon.tsx @@ -13,7 +13,7 @@ interface EtherScanIconProps { export const EtherScanIcon = (props: EtherScanIconProps) => { const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( - props.addressOrTxHash, props.networkId, EtherscanLinkSuffixes.address, + props.addressOrTxHash, props.networkId, EtherscanLinkSuffixes.Address, ); const transactionTooltipId = `${props.addressOrTxHash}-etherscan-icon-tooltip`; return ( diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx index e6b6ea9e26..2ff5b155e8 100644 --- a/packages/website/ts/components/ui/party.tsx +++ b/packages/website/ts/components/ui/party.tsx @@ -45,7 +45,7 @@ export class Party extends React.Component { height: IMAGE_DIMENSION, }; const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( - this.props.address, this.props.networkId, EtherscanLinkSuffixes.address, + this.props.address, this.props.networkId, EtherscanLinkSuffixes.Address, ); const isRegistered = this.props.isInTokenRegistry; const registeredTooltipId = `${this.props.address}-${isRegistered}-registeredTooltip`; diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx index 0ca8ae9d3a..687ba0fcc6 100644 --- a/packages/website/ts/pages/documentation/documentation.tsx +++ b/packages/website/ts/pages/documentation/documentation.tsx @@ -281,7 +281,7 @@ export class Documentation extends return null; } const linkIfExists = utils.getEtherScanLinkIfExists( - contractAddress, constants.networkIdByName[networkName], EtherscanLinkSuffixes.address, + contractAddress, constants.networkIdByName[networkName], EtherscanLinkSuffixes.Address, ); return (
diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index 7992c7a6c5..b8041ece47 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -181,7 +181,7 @@ export class Landing extends React.Component { ); } private renderHero() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const buttonLabelStyle: React.CSSProperties = { textTransform: 'none', fontSize: isSmallScreen ? 12 : 14, @@ -265,8 +265,8 @@ export class Landing extends React.Component { ); } private renderProjects() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; - const isMediumScreen = this.state.screenWidth === ScreenWidths.MD; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + const isMediumScreen = this.state.screenWidth === ScreenWidths.Md; const projectList = _.map(projects, (project: Project, i: number) => { const colWidth = isSmallScreen ? 3 : (isMediumScreen ? 4 : 2 - (i % 2)); return ( @@ -329,7 +329,7 @@ export class Landing extends React.Component { ); } private renderTokenizationSection() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
{ ); } private renderProtocolSection() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
{ ); } private renderBuildingBlocksSection() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const descriptionStyle: React.CSSProperties = { fontFamily: 'Roboto Mono', lineHeight: isSmallScreen ? 1.5 : 2, @@ -540,7 +540,7 @@ export class Landing extends React.Component { ); } private renderBlockChipImage() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
{ ); } private renderTokenCloud() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
{ ); } private renderAssetTypes() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const assetTypes: AssetType[] = [ { title: 'Currency', @@ -601,7 +601,7 @@ export class Landing extends React.Component { return assets; } private renderInfoBoxes() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const boxStyle: React.CSSProperties = { maxWidth: 252, height: 386, @@ -654,7 +654,7 @@ export class Landing extends React.Component { ); } private renderUseCases() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const useCases: UseCase[] = [ { @@ -765,7 +765,7 @@ export class Landing extends React.Component { ); } private renderCallToAction() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.SM; + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const buttonLabelStyle: React.CSSProperties = { textTransform: 'none', fontSize: 15, diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 43bdeaa83a..ccf87ae18e 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -87,7 +87,7 @@ const INITIAL_STATE: State = { // Shared flashMessage: undefined, - providerType: ProviderType.INJECTED, + providerType: ProviderType.Injected, injectedProviderName: '', }; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 58f44dd458..736aa866ce 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,15 +1,6 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; -// Utility function to create a K:V from a list of strings -// Adapted from: https://basarat.gitbooks.io/typescript/content/docs/types/literal-types.html -function strEnum(values: string[]): {[key: string]: string} { - return _.reduce(values, (result, key) => { - result[key] = key; - return result; - }, Object.create(null)); -} - export enum Side { Receive = 'RECEIVE', Deposit = 'DEPOSIT', @@ -229,23 +220,21 @@ export interface ContractEvent { export type InputErrMsg = React.ReactNode | string | undefined; export type ValidatedBigNumberCallback = (isValid: boolean, amount?: BigNumber) => void; -export const ScreenWidths = strEnum([ - 'SM', - 'MD', - 'LG', -]); -export type ScreenWidths = keyof typeof ScreenWidths; +export enum ScreenWidths { + Sm = 'SM', + Md = 'MD', + Lg = 'LG', +} export enum AlertTypes { ERROR, SUCCESS, } -export const EtherscanLinkSuffixes = strEnum([ - 'address', - 'tx', -]); -export type EtherscanLinkSuffixes = keyof typeof EtherscanLinkSuffixes; +export enum EtherscanLinkSuffixes { + Address = 'address', + Tx = 'tx', +} export enum BlockchainErrs { AContractNotDeployedOnNetwork = 'A_CONTRACT_NOT_DEPLOYED_ON_NETWORK', @@ -253,26 +242,25 @@ export enum BlockchainErrs { NoError = 'NO_ERROR', } -export const BlockchainCallErrs = strEnum([ - 'CONTRACT_DOES_NOT_EXIST', - 'USER_HAS_NO_ASSOCIATED_ADDRESSES', - 'UNHANDLED_ERROR', - 'TOKEN_ADDRESS_IS_INVALID', - 'INVALID_SIGNATURE', -]); -export type BlockchainCallErrs = keyof typeof BlockchainCallErrs; +export enum BlockchainCallErrs { + ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST', + UserHasNoAssociatedAddresses = 'USER_HAS_NO_ASSOCIATED_ADDRESSES', + UnhandledError = 'UNHANDLED_ERROR', + TokenAddressIsInvalid = 'TOKEN_ADDRESS_IS_INVALID', +} -export const KindString = strEnum([ - 'Constructor', - 'Property', - 'Method', - 'Interface', - 'Type alias', - 'Variable', - 'Function', - 'Enumeration', -]); -export type KindString = keyof typeof KindString; +// Exception: We don't make the values uppercase because these KindString's need to +// match up those returned by TypeDoc +export enum KindString { + Constructor = 'Constructor', + Property = 'Property', + Method = 'Method', + Interface = 'Interface', + TypeAlias = 'Type alias', + Variable = 'Variable', + Function = 'Function', + Enumeration = 'Enumeration', +} export interface EnumValue { name: string; @@ -468,11 +456,10 @@ export interface MenuSubsectionsBySection { [section: string]: string[]; } -export const ProviderType = strEnum([ - 'INJECTED', - 'LEDGER', -]); -export type ProviderType = keyof typeof ProviderType; +export enum ProviderType { + Injected = 'INJECTED', + Ledger = 'LEDGER', +} export interface Fact { title: string; diff --git a/packages/website/ts/utils/typedoc_utils.ts b/packages/website/ts/utils/typedoc_utils.ts index 9d3b9f8d7a..ed8834a878 100644 --- a/packages/website/ts/utils/typedoc_utils.ts +++ b/packages/website/ts/utils/typedoc_utils.ts @@ -22,7 +22,7 @@ export const typeDocUtils = { isType(entity: TypeDocNode): boolean { return entity.kindString === KindString.Interface || entity.kindString === KindString.Function || - entity.kindString === KindString['Type alias'] || + entity.kindString === KindString.TypeAlias || entity.kindString === KindString.Variable || entity.kindString === KindString.Enumeration; }, @@ -126,7 +126,7 @@ export const typeDocUtils = { case KindString.Function: case KindString.Variable: case KindString.Enumeration: - case KindString['Type alias']: + case KindString.TypeAlias: if (docsInfo.isPublicType(entity.name)) { const customType = typeDocUtils._convertCustomType( entity, docsInfo.sections, sectionName, docsInfo.subPackageName); diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index a6f80c2ab5..211a56d452 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -124,11 +124,11 @@ export const utils = { // This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS // class prefixes. Do not edit these. if (widthInEm > LG_MIN_EM) { - return ScreenWidths.LG; + return ScreenWidths.Lg; } else if (widthInEm > MD_MIN_EM) { - return ScreenWidths.MD; + return ScreenWidths.Md; } else { - return ScreenWidths.SM; + return ScreenWidths.Sm; } }, isUserOnMobile(): boolean { From 0578116f1dbe89a5b3c0c7e5f45948a1434b07b3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 20:51:54 -0500 Subject: [PATCH 041/125] Fix unhoverable help tooltip by renaming `Trade Permissions` to `Allowance` --- packages/website/ts/components/token_balances.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 5338e96099..b111e1ec6c 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -164,7 +164,7 @@ export class TokenBalances extends React.Component @@ -302,7 +302,7 @@ export class TokenBalances extends React.Component Balance -
{!isSmallScreen && 'Trade '}Permissions
+
Allowance
Date: Sun, 17 Dec 2017 20:55:50 -0500 Subject: [PATCH 042/125] Use bigNumberConfigs from @0xproject/utils in website repo --- packages/website/package.json | 1 + packages/website/ts/index.tsx | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/website/package.json b/packages/website/package.json index a6703af7e4..235ff236e9 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -20,6 +20,7 @@ "dependencies": { "0x.js": "~0.27.2", "@0xproject/subproviders": "^0.1.0", + "@0xproject/utils": "^0.1.0", "accounting": "^0.4.1", "basscss": "^8.0.3", "bignumber.js": "~4.1.0", diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 71565fa4d5..69bb03dce0 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -1,6 +1,7 @@ // Polyfills import 'whatwg-fetch'; +import {bigNumberConfigs} from '@0xproject/utils'; import BigNumber from 'bignumber.js'; import {colors, getMuiTheme, MuiThemeProvider} from 'material-ui/styles'; import * as React from 'react'; @@ -22,11 +23,7 @@ import {WebsitePaths} from 'ts/types'; import {constants} from 'ts/utils/constants'; injectTapEventPlugin(); -// By default BigNumber's `toString` method converts to exponential notation if the value has -// more then 20 digits. We want to avoid this behavior, so we set EXPONENTIAL_AT to a high number -BigNumber.config({ - EXPONENTIAL_AT: 1000, -}); +bigNumberConfigs.configure(); // Check if we've introduced an update that requires us to clear the tradeHistory local storage entries tradeHistoryStorage.clearIfRequired(); From 98c01c2f8097c849c53d87a4946edfd4fba94495 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 17 Dec 2017 20:57:39 -0500 Subject: [PATCH 043/125] Remove unused `location` prop --- packages/website/ts/components/footer.tsx | 4 +--- packages/website/ts/components/portal.tsx | 2 +- packages/website/ts/pages/about/about.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 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx index b265230945..7cd9128dff 100644 --- a/packages/website/ts/components/footer.tsx +++ b/packages/website/ts/components/footer.tsx @@ -111,9 +111,7 @@ const titleToIcon: {[title: string]: string} = { 'Reddit': 'reddit.png', }; -export interface FooterProps { - location: Location; -} +export interface FooterProps {} interface FooterState {} diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index 74b1bdec68..4397722881 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -224,7 +224,7 @@ export class Portal extends React.Component { flashMessage={this.props.flashMessage} />
-
+
); } diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 011bbb9d9a..6aeb4ae2d6 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -226,7 +226,7 @@ export class About extends React.Component {
-