import {ZeroEx} from '0x.js'; import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import Divider from 'material-ui/Divider'; import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn, } 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, TokenByAddress, TokenState, TokenStateByAddress, } from 'ts/types'; 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'; const ICON_DIMENSION = 40; const ETHER_ICON_PATH = '/images/ether.png'; const OUTDATED_WETH_ICON_PATH = '/images/wrapped_eth_gray.png'; 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 { 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, 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, configs.ICON_URL_BY_SYMBOL.WETH); return (

ETH Wrapper

About Wrapped ETH
Wrap ETH into an ERC20-compliant Ether token. 1 ETH = 1 WETH.
ETH Token Balance {this._renderActionColumnTitle(isBidirectional)}
ETH
{this.props.userEtherBalance.toFixed(PRECISION)} ETH
{this._renderTokenLink(tokenLabel, etherscanUrl)} {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 {this._renderActionColumnTitle(!isBidirectional)} {this._renderOutdatedWeths(etherToken, etherTokenState)}
); } 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.OUTDATED_WRAPPED_ETHERS, (outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => { 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); const endMoment = moment(timestampMsRange.endTimestampMs); dateRange = `${startMoment.format(DATE_FORMAT)}-${endMoment.format(DATE_FORMAT)}`; } else { dateRange = '-'; } const outdatedEtherToken = { ...etherToken, address: outdatedWETHIfExists.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, 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 ( {this._renderTokenLink(tokenLabel, etherscanUrl)} {isStateLoaded ? `${balanceInEthIfExists} WETH` : } ); }); return rows; } private _renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string) { return ( {_.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: { ...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 = _.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