aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/website/ts/blockchain.ts17
-rw-r--r--packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx16
-rw-r--r--packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx38
-rw-r--r--packages/website/ts/components/eth_wrappers.tsx105
-rw-r--r--packages/website/ts/components/portal.tsx50
-rw-r--r--packages/website/ts/components/send_button.tsx2
-rw-r--r--packages/website/ts/utils/configs.ts7
-rw-r--r--packages/website/ts/utils/constants.ts2
8 files changed, 188 insertions, 49 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index a42b19cff..7a0d546b4 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<boolean> {
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/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
index c8bdced9b..230ac5183 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
+ <div className="left">1 ETH = 1 WETH</div>
+ {this.props.direction === Side.receive &&
+ <div
+ className="right"
+ onClick={this.onMaxClick.bind(this)}
+ style={{color: DARK_BLUE, textDecoration: 'underline', cursor: 'pointer'}}
+ >
+ Max
+ </div>
+ }
</div>
</div>
</div>
@@ -137,6 +146,11 @@ export class EthWethConversionDialog extends
</div>
);
}
+ private onMaxClick() {
+ this.setState({
+ value: this.props.tokenState.balance,
+ });
+ }
private onValueChange(isValid: boolean, amount?: BigNumber) {
this.setState({
value: amount,
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 000000000..d13b2e8ce
--- /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 (
+ <Dialog
+ title="Dedicated Wrapped Ether Section"
+ titleStyle={{fontWeight: 100}}
+ actions={[
+ <FlatButton
+ key="acknowledgeWrapEthSection"
+ label="Sounds good"
+ onTouchTap={props.onToggleDialog.bind(this)}
+ />,
+ ]}
+ open={props.isOpen}
+ onRequestClose={props.onToggleDialog.bind(this)}
+ autoScrollBodyContent={true}
+ modal={true}
+ >
+ <div className="pt2" style={{color: colors.grey700}}>
+ <div>
+ 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.
+ </div>
+ </div>
+ </Dialog>
+ );
+}
diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx
index 58b73b11c..a10313597 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<EthWrappersProps, EthWrappersSt
const etherTokenState = this.props.tokenStateByAddress[etherToken.address];
const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, 18);
const isBidirectional = true;
+ const etherscanUrl = utils.getEtherScanLinkIfExists(
+ etherToken.address, this.props.networkId, EtherscanLinkSuffixes.address,
+ );
+ const tokenLabel = this.renderToken('Wrapped Ether', etherToken.address, constants.iconUrlBySymbol.WETH);
return (
<div className="clearfix lg-px4 md-px4 sm-px2" style={{minHeight: 600}}>
<div className="relative">
@@ -92,7 +99,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
<div className="absolute" style={{top: 0, right: 0}}>
<a
target="_blank"
- href="https://weth.io/"
+ href={constants.WETH_IO_URL}
style={{color: LIGHT_GRAY}}
>
<div className="flex">
@@ -131,8 +138,11 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
style={{width: ICON_DIMENSION, height: ICON_DIMENSION}}
src={ETHER_ICON_PATH}
/>
- <div className="mt2 ml2 sm-hide xs-hide">
- Ether
+ <div
+ className="ml2 sm-hide xs-hide"
+ style={{marginTop: 12}}
+ >
+ ETH
</div>
</div>
</TableRowColumn>
@@ -153,15 +163,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
</TableRow>
<TableRow key="WETH">
<TableRowColumn className="py1">
- <div className="flex">
- <img
- style={{width: ICON_DIMENSION, height: ICON_DIMENSION}}
- src={constants.iconUrlBySymbol.WETH}
- />
- <div className="mt2 ml2 sm-hide xs-hide">
- Wrapped Ether
- </div>
- </div>
+ {this.renderTokenLink(tokenLabel, etherscanUrl)}
</TableRowColumn>
<TableRowColumn>
{wethBalance.toFixed(PRECISION)} WETH
@@ -244,8 +246,11 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
private renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) {
const rows = _.map(configs.outdatedWrappedEthers,
(outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => {
- 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);
@@ -256,26 +261,24 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
}
const outdatedEtherToken = {
...etherToken,
- address: outdatedWETH.address,
+ address: outdatedWETHIfExists.address,
};
- const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETH.address];
- const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETH.address];
+ const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETHIfExists.address];
+ const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETHIfExists.address];
const balanceInEthIfExists = isStateLoaded ?
ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, 18).toFixed(PRECISION) :
undefined;
- const onConversionSuccessful = this.onOutdatedConversionSuccessfulAsync.bind(this, outdatedWETH.address);
+ const onConversionSuccessful = this.onOutdatedConversionSuccessfulAsync.bind(
+ this, outdatedWETHIfExists.address,
+ );
+ const etherscanUrl = utils.getEtherScanLinkIfExists(
+ outdatedWETHIfExists.address, this.props.networkId, EtherscanLinkSuffixes.address,
+ );
+ const tokenLabel = this.renderToken(dateRange, outdatedEtherToken.address, OUTDATED_WETH_ICON_PATH);
return (
- <TableRow key={`weth-${outdatedWETH.address}`}>
+ <TableRow key={`weth-${outdatedWETHIfExists.address}`}>
<TableRowColumn className="py1">
- <div className="flex">
- <img
- style={{width: ICON_DIMENSION, height: ICON_DIMENSION}}
- src={OUTDATED_WETH_ICON_PATH}
- />
- <div className="mt2 ml2 sm-hide xs-hide">
- {dateRange}
- </div>
- </div>
+ {this.renderTokenLink(tokenLabel, etherscanUrl)}
</TableRowColumn>
<TableRowColumn>
{isStateLoaded ?
@@ -301,6 +304,41 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
});
return rows;
}
+ private renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string) {
+ return (
+ <span>
+ {_.isUndefined(etherscanUrl) ?
+ tokenLabel :
+ <a href={etherscanUrl} target="_blank" style={{textDecoration: 'none'}}>
+ {tokenLabel}
+ </a>
+ }
+ </span>
+ );
+ }
+ private renderToken(name: string, address: string, imgPath: string) {
+ const tooltipId = `tooltip-${address}`;
+ return (
+ <div className="flex">
+ <img
+ style={{width: ICON_DIMENSION, height: ICON_DIMENSION}}
+ src={imgPath}
+ />
+ <div
+ className="ml2 sm-hide xs-hide"
+ style={{marginTop: 12}}
+ >
+ <span
+ data-tip={true}
+ data-for={tooltipId}
+ >
+ {name}
+ </span>
+ <ReactTooltip id={tooltipId}>{address}</ReactTooltip>
+ </div>
+ </div>
+ );
+ }
private async onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) {
this.setState({
outdatedWETHAddressToIsStateLoaded: {
@@ -345,9 +383,14 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
});
}
private getOutdatedWETHAddresses(): string[] {
- const outdatedWETHAddresses = _.map(configs.outdatedWrappedEthers, outdatedWrappedEther => {
- 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
diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx
index 74b1bdec6..69a7c1e7f 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<PortalAllProps, PortalAllState> {
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<PortalAllProps, PortalAllState> {
}
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<PortalAllProps, PortalAllState> {
// 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<PortalAllProps, PortalAllState> {
toggleDialogFn={updateShouldBlockchainErrDialogBeOpen}
networkId={this.props.networkId}
/>
+ <WrappedEthSectionNoticeDialog
+ isOpen={this.state.isWethNoticeDialogOpen}
+ onToggleDialog={this.onWethNoticeAccepted.bind(this)}
+ />
<PortalDisclaimerDialog
- isOpen={!this.state.hasAcceptedDisclaimer}
+ isOpen={this.state.isDisclaimerDialogOpen}
onToggleDialog={this.onPortalDisclaimerAccepted.bind(this)}
/>
<FlashMessage
@@ -297,7 +321,13 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
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/components/send_button.tsx b/packages/website/ts/components/send_button.tsx
index b3fd2aeba..d8d3c7f56 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<SendButtonProps, SendButtonState
} else if (!_.includes(errMsg, 'User denied transaction')) {
utils.consoleLog(`Unexpected error encountered: ${err}`);
utils.consoleLog(err.stack);
- await errorReporter.reportAsync(err);
this.props.onError();
+ await errorReporter.reportAsync(err);
}
}
this.setState({
diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts
index 4a08929cf..20efc66be 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: {
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts
index fb81dd9a9..cb2dbef88 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',
@@ -83,6 +84,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',