Files
protocol/packages/website/ts/components/eth_wrappers.tsx
2017-12-14 19:28:49 -06:00

361 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {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,
Side,
Token,
TokenByAddress,
TokenState,
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 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;
dispatcher: Dispatcher;
tokenByAddress: TokenByAddress;
tokenStateByAddress: TokenStateByAddress;
userAddress: string;
userEtherBalance: BigNumber;
}
interface EthWrappersState {
outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded;
outdatedWETHStateByAddress: OutdatedWETHStateByAddress;
}
export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersState> {
constructor(props: EthWrappersProps) {
super(props);
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 etherToken = _.find(tokens, {symbol: 'WETH'});
const etherTokenState = this.props.tokenStateByAddress[etherToken.address];
const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, 18);
const isBidirectional = true;
return (
<div className="clearfix lg-px4 md-px4 sm-px2" style={{minHeight: 600}}>
<div className="relative">
<h3>ETH Wrapper</h3>
<div className="absolute" style={{top: 0, right: 0}}>
<a
target="_blank"
href="https://weth.io/"
style={{color: LIGHT_GRAY}}
>
<div className="flex">
<div>About Wrapped ETH</div>
<div className="pl1">
<i className="zmdi zmdi-open-in-new" />
</div>
</div>
</a>
</div>
</div>
<Divider />
<div>
<div className="py2">
Wrap ETH into an ERC20-compliant Ether token. 1 ETH = 1 WETH.
</div>
<div>
<Table
selectable={false}
style={{backgroundColor: colors.grey50}}
>
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
<TableRow>
<TableHeaderColumn>ETH Token</TableHeaderColumn>
<TableHeaderColumn>Balance</TableHeaderColumn>
<TableHeaderColumn className="center">
{this.renderActionColumnTitle(isBidirectional)}
</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false}>
<TableRow key="ETH">
<TableRowColumn className="py1">
<div className="flex">
<img
style={{width: ICON_DIMENSION, height: ICON_DIMENSION}}
src={ETHER_ICON_PATH}
/>
<div className="mt2 ml2 sm-hide xs-hide">
Ether
</div>
</div>
</TableRowColumn>
<TableRowColumn>
{this.props.userEtherBalance.toFixed(PRECISION)} ETH
</TableRowColumn>
<TableRowColumn>
<EthWethConversionButton
isOutdatedWrappedEther={false}
direction={Side.deposit}
ethToken={etherToken}
ethTokenState={etherTokenState}
dispatcher={this.props.dispatcher}
blockchain={this.props.blockchain}
userEtherBalance={this.props.userEtherBalance}
/>
</TableRowColumn>
</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>
</TableRowColumn>
<TableRowColumn>
{wethBalance.toFixed(PRECISION)} WETH
</TableRowColumn>
<TableRowColumn>
<EthWethConversionButton
isOutdatedWrappedEther={false}
direction={Side.receive}
ethToken={etherToken}
ethTokenState={etherTokenState}
dispatcher={this.props.dispatcher}
blockchain={this.props.blockchain}
userEtherBalance={this.props.userEtherBalance}
/>
</TableRowColumn>
</TableRow>
</TableBody>
</Table>
</div>
</div>
<div>
<h4>Outdated WETH</h4>
<Divider />
<div className="pt2" style={{lineHeight: 1.5}}>
The{' '}
<a
href="https://blog.0xproject.com/canonical-weth-a9aa7d0279dd"
target="_blank"
>
canonical WETH
</a> contract is updated when necessary.
Unwrap outdated WETH in order to retrieve your ETH and move it
to the updated WETH token.
</div>
<div>
<Table
selectable={false}
style={{backgroundColor: colors.grey50}}
>
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
<TableRow>
<TableHeaderColumn>WETH Version</TableHeaderColumn>
<TableHeaderColumn>Balance</TableHeaderColumn>
<TableHeaderColumn className="center">
{this.renderActionColumnTitle(!isBidirectional)}
</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false}>
{this.renderOutdatedWeths(etherToken, etherTokenState)}
</TableBody>
</Table>
</div>
</div>
</div>
);
}
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 (
<div className="flex mx-auto" style={{width: 85}}>
<div style={{paddingTop: 3}}>{leftSymbol}</div>
<div className="px1">
<i
style={{fontSize: 18}}
className={`zmdi ${iconClass}`}
/>
</div>
<div style={{paddingTop: 3}}>{rightSymbol}</div>
</div>
);
}
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 (
<TableRow key={`weth-${outdatedWETH.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>
</TableRowColumn>
<TableRowColumn>
{isStateLoaded ?
`${balanceInEthIfExists} WETH` :
<i className="zmdi zmdi-spinner zmdi-hc-spin" />
}
</TableRowColumn>
<TableRowColumn>
<EthWethConversionButton
isDisabled={!isStateLoaded}
isOutdatedWrappedEther={true}
direction={Side.receive}
ethToken={outdatedEtherToken}
ethTokenState={outdatedEtherTokenState}
dispatcher={this.props.dispatcher}
blockchain={this.props.blockchain}
userEtherBalance={this.props.userEtherBalance}
onConversionSuccessful={onConversionSuccessful}
/>
</TableRowColumn>
</TableRow>
);
});
return rows;
}
private async onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) {
this.setState({
outdatedWETHAddressToIsStateLoaded: {
...this.state.outdatedWETHAddressToIsStateLoaded,
[outdatedWETHAddress]: false,
},
});
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
this.props.userAddress, outdatedWETHAddress,
);
this.setState({
outdatedWETHAddressToIsStateLoaded: {
...this.state.outdatedWETHAddressToIsStateLoaded,
[outdatedWETHAddress]: true,
},
outdatedWETHStateByAddress: {
...this.state.outdatedWETHStateByAddress,
[outdatedWETHAddress]: {
balance,
allowance,
},
},
});
}
private async fetchOutdatedWETHStateAsync() {
const outdatedWETHAddresses = this.getOutdatedWETHAddresses();
const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {};
const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {};
for (const address of outdatedWETHAddresses) {
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
this.props.userAddress, address,
);
outdatedWETHStateByAddress[address] = {
balance,
allowance,
};
outdatedWETHAddressToIsStateLoaded[address] = true;
}
this.setState({
outdatedWETHStateByAddress,
outdatedWETHAddressToIsStateLoaded,
});
}
private getOutdatedWETHAddresses(): string[] {
const outdatedWETHAddresses = _.map(configs.outdatedWrappedEthers, outdatedWrappedEther => {
return outdatedWrappedEther[this.props.networkId].address;
});
return outdatedWETHAddresses;
}
} // tslint:disable:max-file-line-count