aboutsummaryrefslogblamecommitdiffstats
path: root/packages/website/ts/components/eth_wrappers.tsx
blob: ccbed4188259bbf881b4c8e18b8b8b6a25fcb9fc (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                    
                                                                                 



                                                                               


                                    
                   
               








                                                      
                             




                                                               






                                              









                                             



                                                                           



                                                                                      













                                                                                          


                                

                                                        


                                                           


                                                                                   
                                     

                                                                                    
















                                                                           


                                         
                                                                                     









                                                                                            

                                                                                       


















                                                                                                       
                                                    







                                                                                          

















                                                                                                       
                                                    







                                                                                          






                                                     
                     





















                                                                                            

                                                                                        







                                                                                       



                      





















                                                                






















                                                                                                                      








                                                                                       
                                           



                                     



                                                                            
                                     
                                    









                                                                           






                                     






















                                                                                                  
     























                                                                                                      

                                       
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