aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website')
-rw-r--r--packages/website/ts/blockchain.ts20
-rw-r--r--packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx16
-rw-r--r--packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx4
-rw-r--r--packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx38
-rw-r--r--packages/website/ts/components/eth_wrappers.tsx106
-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.ts1
9 files changed, 193 insertions, 51 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index d3bf6dda4..481917934 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -158,6 +158,14 @@ 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.
+ // TODO: Remove this hack once we've updated the TokenRegistries
+ // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz
+ if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN &&
+ tokenAddress === configs.NEW_WRAPPED_ETHERS[this.networkId]) {
+ return true;
+ }
const tokenIfExists = await this.zeroEx.tokenRegistry.getTokenIfExistsAsync(tokenAddress);
return !_.isUndefined(tokenIfExists);
}
@@ -597,13 +605,13 @@ export class Blockchain {
// 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
+ // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz
let address = t.address;
- if (t.symbol === 'WETH') {
- if (this.networkId === 1) {
- address = '0xe495bcacaf29a0eb00fb67b86e9cd2a994dd55d8';
- } else if (this.networkId === 42) {
- address = '0x739e78d6bebbdf24105a5145fa04436589d1cbd9';
- }
+ if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && t.symbol === 'WETH') {
+ const newEtherTokenAddressIfExists = configs.NEW_WRAPPED_ETHERS[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 44c23e4b0..25ded40e9 100644
--- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
+++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
@@ -108,7 +108,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>
@@ -136,6 +145,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/portal_disclaimer_dialog.tsx b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
index 2f824ab44..ffe55794f 100644
--- a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
+++ b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
@@ -17,11 +17,11 @@ export function PortalDisclaimerDialog(props: PortalDisclaimerDialogProps) {
<FlatButton
key="portalAgree"
label="I Agree"
- onTouchTap={props.onToggleDialog.bind(this)}
+ onTouchTap={props.onToggleDialog}
/>,
]}
open={props.isOpen}
- onRequestClose={props.onToggleDialog.bind(this)}
+ onRequestClose={props.onToggleDialog}
autoScrollBodyContent={true}
modal={true}
>
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..3f485ce4f
--- /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}
+ />,
+ ]}
+ open={props.isOpen}
+ onRequestClose={props.onToggleDialog}
+ autoScrollBodyContent={true}
+ modal={true}
+ >
+ <div className="pt2" style={{color: colors.grey700}}>
+ <div>
+ We have recently updated the Wrapped Ether token (WETH) 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 59353d18c..da95aa6fd 100644
--- a/packages/website/ts/components/eth_wrappers.tsx
+++ b/packages/website/ts/components/eth_wrappers.tsx
@@ -12,10 +12,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 {
import {colors} from 'ts/utils/colors';
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';
@@ -84,6 +87,10 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
const etherTokenState = this.props.tokenStateByAddress[etherToken.address];
const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, constants.DECIMAL_PLACES_ETH);
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">
@@ -91,7 +98,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: colors.grey}}
>
<div className="flex">
@@ -130,8 +137,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>
@@ -152,15 +162,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={configs.ICON_URL_BY_SYMBOL.WETH}
- />
- <div className="mt2 ml2 sm-hide xs-hide">
- Wrapped Ether
- </div>
- </div>
+ {this.renderTokenLink(tokenLabel, etherscanUrl)}
</TableRowColumn>
<TableRowColumn>
{wethBalance.toFixed(PRECISION)} WETH
@@ -243,8 +245,11 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
private renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) {
const rows = _.map(configs.OUTDATED_WRAPPED_ETHERS,
(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);
@@ -255,28 +260,26 @@ 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, constants.DECIMAL_PLACES_ETH,
).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 ?
@@ -302,6 +305,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: {
@@ -346,9 +384,15 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
});
}
private getOutdatedWETHAddresses(): string[] {
- const outdatedWETHAddresses = _.map(configs.OUTDATED_WRAPPED_ETHERS, outdatedWrappedEther => {
- return outdatedWrappedEther[this.props.networkId].address;
- });
+ const outdatedWETHAddresses = _.compact(_.map(configs.OUTDATED_WRAPPED_ETHERS,
+ 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 2f6f9912a..08028d15e 100644
--- a/packages/website/ts/components/portal.tsx
+++ b/packages/website/ts/components/portal.tsx
@@ -7,6 +7,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.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE);
+ 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.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER);
+ 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.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER);
- 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
@@ -220,8 +240,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
@@ -302,7 +326,13 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
private onPortalDisclaimerAccepted() {
localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, '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 1fc300964..23e61e77e 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 8921c68a4..b66348416 100644
--- a/packages/website/ts/utils/configs.ts
+++ b/packages/website/ts/utils/configs.ts
@@ -96,6 +96,12 @@ export const configs = {
IS_MAINNET_ENABLED: true,
LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22',
LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2017-12-13',
+ // NEW_WRAPPED_ETHERS is temporary until we remove the SHOULD_DEPRECATE_OLD_WETH_TOKEN flag
+ // and add the new WETHs to the tokenRegistry
+ NEW_WRAPPED_ETHERS: {
+ 1: '0xe495bcacaf29a0eb00fb67b86e9cd2a994dd55d8',
+ 42: '0x739e78d6bebbdf24105a5145fa04436589d1cbd9',
+ } as {[networkId: string]: string},
OUTDATED_WRAPPED_ETHERS: [
{
42: {
@@ -123,5 +129,6 @@ export const configs = {
`https://kovan.infura.io/${INFURA_API_KEY}`,
],
} as PublicNodeUrlsByNetworkId,
+ SHOULD_DEPRECATE_OLD_WETH_TOKEN: true,
SYMBOLS_OF_MINTABLE_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'],
};
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts
index 0e4a1056a..e6b51c452 100644
--- a/packages/website/ts/utils/constants.ts
+++ b/packages/website/ts/utils/constants.ts
@@ -16,6 +16,7 @@ export const constants = {
HOME_SCROLL_DURATION_MS: 500,
HTTP_NO_CONTENT_STATUS_CODE: 204,
LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER: 'didAcceptPortalDisclaimer',
+ LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE: 'hasDismissedWethNotice',
MAKER_FEE: new BigNumber(0),
MAINNET_NAME: 'Main network',
MINT_AMOUNT: new BigNumber('100000000000000000000'),