528 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			528 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0x/react-shared';
 | 
						|
import { BigNumber, errorUtils } from '@0x/utils';
 | 
						|
import * as _ from 'lodash';
 | 
						|
 | 
						|
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
 | 
						|
import * as React from 'react';
 | 
						|
import firstBy from 'thenby';
 | 
						|
 | 
						|
import { Blockchain } from 'ts/blockchain';
 | 
						|
import { AccountConnection } from 'ts/components/ui/account_connection';
 | 
						|
import { Balance } from 'ts/components/ui/balance';
 | 
						|
import { Container } from 'ts/components/ui/container';
 | 
						|
import { DropDown, DropdownMouseEvent } from 'ts/components/ui/drop_down';
 | 
						|
import { IconButton } from 'ts/components/ui/icon_button';
 | 
						|
import { Identicon } from 'ts/components/ui/identicon';
 | 
						|
import { Island } from 'ts/components/ui/island';
 | 
						|
import { PointerDirection } from 'ts/components/ui/pointer';
 | 
						|
import {
 | 
						|
    CopyAddressSimpleMenuItem,
 | 
						|
    DifferentWalletSimpleMenuItem,
 | 
						|
    GoToAccountManagementSimpleMenuItem,
 | 
						|
    SimpleMenu,
 | 
						|
    SimpleMenuItem,
 | 
						|
} from 'ts/components/ui/simple_menu';
 | 
						|
import { Text } from 'ts/components/ui/text';
 | 
						|
import { TokenIcon } from 'ts/components/ui/token_icon';
 | 
						|
import { BodyOverlay } from 'ts/components/wallet/body_overlay';
 | 
						|
import { NullTokenRow } from 'ts/components/wallet/null_token_row';
 | 
						|
import { PlaceHolder } from 'ts/components/wallet/placeholder';
 | 
						|
import { StandardIconRow } from 'ts/components/wallet/standard_icon_row';
 | 
						|
import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item';
 | 
						|
import { AllowanceStateToggle } from 'ts/containers/inputs/allowance_state_toggle';
 | 
						|
import { Dispatcher } from 'ts/redux/dispatcher';
 | 
						|
import { colors } from 'ts/style/colors';
 | 
						|
import {
 | 
						|
    AccountState,
 | 
						|
    BlockchainErrs,
 | 
						|
    ProviderType,
 | 
						|
    ScreenWidths,
 | 
						|
    Side,
 | 
						|
    Token,
 | 
						|
    TokenByAddress,
 | 
						|
    TokenState,
 | 
						|
    TokenStateByAddress,
 | 
						|
} from 'ts/types';
 | 
						|
import { analytics } from 'ts/utils/analytics';
 | 
						|
import { constants } from 'ts/utils/constants';
 | 
						|
import { utils } from 'ts/utils/utils';
 | 
						|
 | 
						|
export interface WalletProps {
 | 
						|
    userAddress: string;
 | 
						|
    networkId: number;
 | 
						|
    blockchain: Blockchain;
 | 
						|
    blockchainIsLoaded: boolean;
 | 
						|
    blockchainErr: BlockchainErrs;
 | 
						|
    dispatcher: Dispatcher;
 | 
						|
    tokenByAddress: TokenByAddress;
 | 
						|
    trackedTokens: Token[];
 | 
						|
    userEtherBalanceInWei?: BigNumber;
 | 
						|
    lastForceTokenStateRefetch: number;
 | 
						|
    injectedProviderName: string;
 | 
						|
    providerType: ProviderType;
 | 
						|
    screenWidth: ScreenWidths;
 | 
						|
    location: Location;
 | 
						|
    trackedTokenStateByAddress: TokenStateByAddress;
 | 
						|
    onToggleLedgerDialog: () => void;
 | 
						|
    onAddToken: () => void;
 | 
						|
    onRemoveToken: () => void;
 | 
						|
    refetchTokenStateAsync: (tokenAddress: string) => Promise<void>;
 | 
						|
    style: React.CSSProperties;
 | 
						|
    toggleTooltipDirection?: PointerDirection;
 | 
						|
}
 | 
						|
 | 
						|
interface WalletState {
 | 
						|
    wrappedEtherDirection?: Side;
 | 
						|
    isHoveringSidebar: boolean;
 | 
						|
}
 | 
						|
 | 
						|
interface AllowanceStateToggleConfig {
 | 
						|
    token: Token;
 | 
						|
    tokenState: TokenState;
 | 
						|
}
 | 
						|
 | 
						|
interface AccessoryItemConfig {
 | 
						|
    wrappedEtherDirection?: Side;
 | 
						|
    allowanceStateToggleConfig?: AllowanceStateToggleConfig;
 | 
						|
}
 | 
						|
 | 
						|
const ETHER_ICON_PATH = '/images/ether.png';
 | 
						|
const ICON_DIMENSION = 28;
 | 
						|
const BODY_ITEM_KEY = 'BODY';
 | 
						|
const HEADER_ITEM_KEY = 'HEADER';
 | 
						|
const ETHER_ITEM_KEY = 'ETHER';
 | 
						|
const WRAP_ROW_ALLOWANCE_TOGGLE_WIDTH = 67;
 | 
						|
const ALLOWANCE_TOGGLE_WIDTH = 56;
 | 
						|
const PLACEHOLDER_COLOR = colors.grey300;
 | 
						|
const LOADING_ROWS_COUNT = 6;
 | 
						|
 | 
						|
export class Wallet extends React.Component<WalletProps, WalletState> {
 | 
						|
    public static defaultProps = {
 | 
						|
        style: {},
 | 
						|
    };
 | 
						|
    constructor(props: WalletProps) {
 | 
						|
        super(props);
 | 
						|
        this.state = {
 | 
						|
            wrappedEtherDirection: undefined,
 | 
						|
            isHoveringSidebar: false,
 | 
						|
        };
 | 
						|
    }
 | 
						|
    public componentDidUpdate(prevProps: WalletProps): void {
 | 
						|
        const currentTrackedTokens = this.props.trackedTokens;
 | 
						|
        const differentTrackedTokens = _.difference(currentTrackedTokens, prevProps.trackedTokens);
 | 
						|
        const firstDifferentTrackedToken = _.head(differentTrackedTokens);
 | 
						|
        // check if there is only one different token, and if that token is a member of the current tracked tokens
 | 
						|
        // this means that the token was added, not removed
 | 
						|
        if (
 | 
						|
            !_.isUndefined(firstDifferentTrackedToken) &&
 | 
						|
            _.size(differentTrackedTokens) === 1 &&
 | 
						|
            _.includes(currentTrackedTokens, firstDifferentTrackedToken)
 | 
						|
        ) {
 | 
						|
            document.getElementById(firstDifferentTrackedToken.address).scrollIntoView();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    public render(): React.ReactNode {
 | 
						|
        return (
 | 
						|
            <Island className="flex flex-column wallet" style={this.props.style}>
 | 
						|
                {this._isBlockchainReady() ? this._renderLoadedRows() : this._renderLoadingRows()}
 | 
						|
            </Island>
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderLoadingRows(): React.ReactNode {
 | 
						|
        return _.concat(this._renderLoadingHeaderRows(), this._renderLoadingBodyRows());
 | 
						|
    }
 | 
						|
    private _renderLoadingHeaderRows(): React.ReactElement<{}> {
 | 
						|
        return this._renderPlainHeaderRow('Loading...');
 | 
						|
    }
 | 
						|
    private _renderLoadingBodyRows(): React.ReactElement<{}> {
 | 
						|
        const bodyStyle = this._getBodyStyle();
 | 
						|
        const loadingRowsRange = _.range(LOADING_ROWS_COUNT);
 | 
						|
        return (
 | 
						|
            <div key={BODY_ITEM_KEY} className="flex flex-column" style={bodyStyle}>
 | 
						|
                {_.map(loadingRowsRange, index => {
 | 
						|
                    return <NullTokenRow key={index} iconDimension={ICON_DIMENSION} fillColor={PLACEHOLDER_COLOR} />;
 | 
						|
                })}
 | 
						|
                <Container
 | 
						|
                    className="flex items-center"
 | 
						|
                    position="absolute"
 | 
						|
                    width="100%"
 | 
						|
                    height="100%"
 | 
						|
                    maxHeight={bodyStyle.maxHeight}
 | 
						|
                >
 | 
						|
                    <div className="mx-auto">
 | 
						|
                        <BodyOverlay
 | 
						|
                            dispatcher={this.props.dispatcher}
 | 
						|
                            userAddress={this.props.userAddress}
 | 
						|
                            injectedProviderName={this.props.injectedProviderName}
 | 
						|
                            providerType={this.props.providerType}
 | 
						|
                            onToggleLedgerDialog={this.props.onToggleLedgerDialog}
 | 
						|
                            blockchain={this.props.blockchain}
 | 
						|
                            blockchainIsLoaded={this.props.blockchainIsLoaded}
 | 
						|
                        />
 | 
						|
                    </div>
 | 
						|
                </Container>
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderLoadedRows(): React.ReactNode {
 | 
						|
        const isAddressAvailable = !_.isEmpty(this.props.userAddress);
 | 
						|
        return isAddressAvailable
 | 
						|
            ? _.concat(this._renderConnectedHeaderRows(), this._renderBody())
 | 
						|
            : _.concat(this._renderDisconnectedHeaderRows(), this._renderLoadingBodyRows());
 | 
						|
    }
 | 
						|
    private _renderDisconnectedHeaderRows(): React.ReactElement<{}> {
 | 
						|
        const isExternallyInjectedProvider = utils.isExternallyInjected(
 | 
						|
            this.props.providerType,
 | 
						|
            this.props.injectedProviderName,
 | 
						|
        );
 | 
						|
        const text = isExternallyInjectedProvider ? 'Please unlock MetaMask...' : 'Please connect a wallet...';
 | 
						|
        return this._renderPlainHeaderRow(text);
 | 
						|
    }
 | 
						|
    private _renderPlainHeaderRow(text: string): React.ReactElement<{}> {
 | 
						|
        return (
 | 
						|
            <StandardIconRow
 | 
						|
                key={HEADER_ITEM_KEY}
 | 
						|
                icon={<ActionAccountBalanceWallet color={colors.grey} />}
 | 
						|
                main={
 | 
						|
                    <Text fontSize="16px" fontColor={colors.grey}>
 | 
						|
                        {text}
 | 
						|
                    </Text>
 | 
						|
                    // https://github.com/palantir/tslint-react/issues/140
 | 
						|
                    // tslint:disable-next-line:jsx-curly-spacing
 | 
						|
                }
 | 
						|
                minHeight="60px"
 | 
						|
                backgroundColor={colors.white}
 | 
						|
            />
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderConnectedHeaderRows(): React.ReactElement<{}> {
 | 
						|
        const isMobile = this.props.screenWidth === ScreenWidths.Sm;
 | 
						|
        const userAddress = this.props.userAddress;
 | 
						|
        const accountState = this._getAccountState();
 | 
						|
        const main = (
 | 
						|
            <div className="flex flex-column">
 | 
						|
                <Text fontSize="16px" lineHeight="19px" fontWeight={500}>
 | 
						|
                    {utils.getAddressBeginAndEnd(userAddress)}
 | 
						|
                </Text>
 | 
						|
                <AccountConnection accountState={accountState} injectedProviderName={this.props.injectedProviderName} />
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
        const onClick = _.noop.bind(_);
 | 
						|
        const accessory = (
 | 
						|
            <DropDown
 | 
						|
                activeNode={
 | 
						|
                    // this container gives the menu button more of a hover target for the drop down
 | 
						|
                    // it prevents accidentally closing the menu by moving off of the button
 | 
						|
                    <Container paddingLeft="100px" paddingRight="15px">
 | 
						|
                        <Text
 | 
						|
                            className="zmdi zmdi-more-horiz"
 | 
						|
                            Tag="i"
 | 
						|
                            fontSize="32px"
 | 
						|
                            fontFamily="Material-Design-Iconic-Font"
 | 
						|
                            fontColor={colors.darkGrey}
 | 
						|
                            onClick={onClick}
 | 
						|
                            hoverColor={colors.mediumBlue}
 | 
						|
                        />
 | 
						|
                    </Container>
 | 
						|
                }
 | 
						|
                popoverContent={
 | 
						|
                    <SimpleMenu minWidth="150px">
 | 
						|
                        <CopyAddressSimpleMenuItem userAddress={this.props.userAddress} />
 | 
						|
                        {!isMobile && <DifferentWalletSimpleMenuItem onClick={this.props.onToggleLedgerDialog} />}
 | 
						|
                        <SimpleMenuItem displayText="Add Tokens..." onClick={this.props.onAddToken} />
 | 
						|
                        <SimpleMenuItem displayText="Remove Tokens..." onClick={this.props.onRemoveToken} />
 | 
						|
                        {!isMobile && <GoToAccountManagementSimpleMenuItem />}
 | 
						|
                    </SimpleMenu>
 | 
						|
                }
 | 
						|
                anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
 | 
						|
                targetOrigin={{ horizontal: 'right', vertical: 'top' }}
 | 
						|
                zDepth={1}
 | 
						|
                activateEvent={DropdownMouseEvent.Click}
 | 
						|
                closeEvent={DropdownMouseEvent.Click}
 | 
						|
            />
 | 
						|
        );
 | 
						|
        return (
 | 
						|
            <StandardIconRow
 | 
						|
                key={HEADER_ITEM_KEY}
 | 
						|
                icon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
 | 
						|
                main={main}
 | 
						|
                accessory={accessory}
 | 
						|
                minHeight="60px"
 | 
						|
                backgroundColor={colors.white}
 | 
						|
            />
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderBody(): React.ReactElement<{}> {
 | 
						|
        const bodyStyle = this._getBodyStyle();
 | 
						|
        return (
 | 
						|
            <div
 | 
						|
                style={bodyStyle}
 | 
						|
                key={BODY_ITEM_KEY}
 | 
						|
                onMouseEnter={this._onSidebarHover.bind(this)}
 | 
						|
                onMouseLeave={this._onSidebarHoverOff.bind(this)}
 | 
						|
            >
 | 
						|
                {this._renderEthRows()}
 | 
						|
                {this._renderTokenRows()}
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _getBodyStyle(): React.CSSProperties {
 | 
						|
        return {
 | 
						|
            overflow: 'auto',
 | 
						|
            WebkitOverflowScrolling: 'touch',
 | 
						|
            position: 'relative',
 | 
						|
            overflowY: this.state.isHoveringSidebar ? 'scroll' : 'hidden',
 | 
						|
            marginRight: this.state.isHoveringSidebar ? 0 : 4,
 | 
						|
            minHeight: '250px',
 | 
						|
            maxHeight: !utils.isMobileWidth(this.props.screenWidth) ? 'calc(90vh - 300px)' : undefined,
 | 
						|
        };
 | 
						|
    }
 | 
						|
    private _onSidebarHover(_event: React.FormEvent<HTMLInputElement>): void {
 | 
						|
        this.setState({
 | 
						|
            isHoveringSidebar: true,
 | 
						|
        });
 | 
						|
    }
 | 
						|
    private _onSidebarHoverOff(): void {
 | 
						|
        this.setState({
 | 
						|
            isHoveringSidebar: false,
 | 
						|
        });
 | 
						|
    }
 | 
						|
    private _renderEthRows(): React.ReactNode {
 | 
						|
        const icon = <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />;
 | 
						|
        const primaryText = this._renderAmount(
 | 
						|
            this.props.userEtherBalanceInWei || new BigNumber(0),
 | 
						|
            constants.DECIMAL_PLACES_ETH,
 | 
						|
            constants.ETHER_SYMBOL,
 | 
						|
            _.isUndefined(this.props.userEtherBalanceInWei),
 | 
						|
        );
 | 
						|
        const etherToken = this._getEthToken();
 | 
						|
        const etherTokenState = this.props.trackedTokenStateByAddress[etherToken.address];
 | 
						|
        const etherPrice = etherTokenState.price;
 | 
						|
        const secondaryText = this._renderValue(
 | 
						|
            this.props.userEtherBalanceInWei || new BigNumber(0),
 | 
						|
            constants.DECIMAL_PLACES_ETH,
 | 
						|
            etherPrice,
 | 
						|
            _.isUndefined(this.props.userEtherBalanceInWei) || !etherTokenState.isLoaded,
 | 
						|
        );
 | 
						|
        const accessoryItemConfig = {
 | 
						|
            wrappedEtherDirection: Side.Deposit,
 | 
						|
        };
 | 
						|
        const key = ETHER_ITEM_KEY;
 | 
						|
        return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig);
 | 
						|
    }
 | 
						|
    private _renderTokenRows(): React.ReactNode {
 | 
						|
        const trackedTokens = this.props.trackedTokens;
 | 
						|
        const trackedTokensStartingWithEtherToken = trackedTokens.sort(
 | 
						|
            firstBy((t: Token) => t.symbol !== constants.ETHER_TOKEN_SYMBOL)
 | 
						|
                .thenBy((t: Token) => t.symbol !== constants.ZRX_TOKEN_SYMBOL)
 | 
						|
                .thenBy('trackedTimestamp'),
 | 
						|
        );
 | 
						|
        return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this));
 | 
						|
    }
 | 
						|
    private _renderTokenRow(token: Token): React.ReactNode {
 | 
						|
        const tokenState = this.props.trackedTokenStateByAddress[token.address];
 | 
						|
        if (_.isUndefined(tokenState)) {
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
        const tokenLink = sharedUtils.getEtherScanLinkIfExists(
 | 
						|
            token.address,
 | 
						|
            this.props.networkId,
 | 
						|
            EtherscanLinkSuffixes.Address,
 | 
						|
        );
 | 
						|
        const icon = <TokenIcon token={token} diameter={ICON_DIMENSION} link={tokenLink} />;
 | 
						|
        const isWeth = token.symbol === constants.ETHER_TOKEN_SYMBOL;
 | 
						|
        const wrappedEtherDirection = isWeth ? Side.Receive : undefined;
 | 
						|
        const primaryText = this._renderAmount(tokenState.balance, token.decimals, token.symbol, !tokenState.isLoaded);
 | 
						|
        const secondaryText = this._renderValue(
 | 
						|
            tokenState.balance,
 | 
						|
            token.decimals,
 | 
						|
            tokenState.price,
 | 
						|
            !tokenState.isLoaded,
 | 
						|
        );
 | 
						|
        const accessoryItemConfig: AccessoryItemConfig = {
 | 
						|
            wrappedEtherDirection,
 | 
						|
            allowanceStateToggleConfig: {
 | 
						|
                token,
 | 
						|
                tokenState,
 | 
						|
            },
 | 
						|
        };
 | 
						|
        const key = token.address;
 | 
						|
        return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig);
 | 
						|
    }
 | 
						|
    private _renderBalanceRow(
 | 
						|
        key: string,
 | 
						|
        icon: React.ReactNode,
 | 
						|
        primaryText: React.ReactNode,
 | 
						|
        secondaryText: React.ReactNode,
 | 
						|
        accessoryItemConfig: AccessoryItemConfig,
 | 
						|
        className?: string,
 | 
						|
    ): React.ReactNode {
 | 
						|
        const shouldShowWrapEtherItem =
 | 
						|
            !_.isUndefined(this.state.wrappedEtherDirection) &&
 | 
						|
            this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection &&
 | 
						|
            !_.isUndefined(this.props.userEtherBalanceInWei);
 | 
						|
        const etherToken = this._getEthToken();
 | 
						|
        const wrapEtherItem = shouldShowWrapEtherItem ? (
 | 
						|
            <WrapEtherItem
 | 
						|
                userAddress={this.props.userAddress}
 | 
						|
                networkId={this.props.networkId}
 | 
						|
                blockchain={this.props.blockchain}
 | 
						|
                dispatcher={this.props.dispatcher}
 | 
						|
                userEtherBalanceInWei={this.props.userEtherBalanceInWei}
 | 
						|
                direction={accessoryItemConfig.wrappedEtherDirection}
 | 
						|
                etherToken={etherToken}
 | 
						|
                lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
 | 
						|
                onConversionSuccessful={this._closeWrappedEtherActionRow.bind(this)}
 | 
						|
                // tslint:disable:jsx-no-lambda
 | 
						|
                refetchEthTokenStateAsync={async () => this.props.refetchTokenStateAsync(etherToken.address)}
 | 
						|
            />
 | 
						|
        ) : null;
 | 
						|
        return (
 | 
						|
            <div id={key} key={key} className={`flex flex-column ${className || ''}`}>
 | 
						|
                {this.state.wrappedEtherDirection === Side.Receive && wrapEtherItem}
 | 
						|
                <StandardIconRow
 | 
						|
                    icon={icon}
 | 
						|
                    main={
 | 
						|
                        <div className="flex flex-column">
 | 
						|
                            {primaryText}
 | 
						|
                            <Container marginTop="3px">{secondaryText}</Container>
 | 
						|
                        </div>
 | 
						|
                    }
 | 
						|
                    accessory={this._renderAccessoryItems(accessoryItemConfig)}
 | 
						|
                />
 | 
						|
                {this.state.wrappedEtherDirection === Side.Deposit && wrapEtherItem}
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderAccessoryItems(config: AccessoryItemConfig): React.ReactElement<{}> {
 | 
						|
        const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherDirection);
 | 
						|
        const shouldShowToggle = !_.isUndefined(config.allowanceStateToggleConfig);
 | 
						|
        // if we don't have a toggle, we still want some space to the right of the "wrap" button so that it aligns with
 | 
						|
        // the "unwrap" button in the row below
 | 
						|
        const isWrapEtherRow = shouldShowWrappedEtherAction && config.wrappedEtherDirection === Side.Deposit;
 | 
						|
        const width = isWrapEtherRow ? WRAP_ROW_ALLOWANCE_TOGGLE_WIDTH : ALLOWANCE_TOGGLE_WIDTH;
 | 
						|
        const toggle = (
 | 
						|
            <Container className="flex justify-center" width={width}>
 | 
						|
                {shouldShowToggle && this._renderAllowanceToggle(config.allowanceStateToggleConfig)}
 | 
						|
            </Container>
 | 
						|
        );
 | 
						|
        return (
 | 
						|
            <div className="flex items-center">
 | 
						|
                <div className="flex-auto">
 | 
						|
                    {shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherDirection)}
 | 
						|
                </div>
 | 
						|
                <div className="flex-last pl2">{toggle}</div>
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderAllowanceToggle(config: AllowanceStateToggleConfig): React.ReactNode {
 | 
						|
        // TODO: Error handling
 | 
						|
        return (
 | 
						|
            <AllowanceStateToggle
 | 
						|
                blockchain={this.props.blockchain}
 | 
						|
                token={config.token}
 | 
						|
                tokenState={config.tokenState}
 | 
						|
                tooltipDirection={this.props.toggleTooltipDirection}
 | 
						|
                refetchTokenStateAsync={async () => this.props.refetchTokenStateAsync(config.token.address)}
 | 
						|
            />
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderAmount(
 | 
						|
        amount: BigNumber,
 | 
						|
        decimals: number,
 | 
						|
        symbol: string,
 | 
						|
        isLoading: boolean = false,
 | 
						|
    ): React.ReactNode {
 | 
						|
        if (isLoading) {
 | 
						|
            return (
 | 
						|
                <PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}>
 | 
						|
                    <Text fontSize="16px" fontWeight="bold" lineHeight="1em">
 | 
						|
                        0.00 XXX
 | 
						|
                    </Text>
 | 
						|
                </PlaceHolder>
 | 
						|
            );
 | 
						|
        } else {
 | 
						|
            return <Balance amount={amount} decimals={decimals} symbol={symbol} />;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    private _renderValue(
 | 
						|
        amount: BigNumber,
 | 
						|
        decimals: number,
 | 
						|
        price?: BigNumber,
 | 
						|
        isLoading: boolean = false,
 | 
						|
    ): React.ReactNode {
 | 
						|
        const result = !isLoading
 | 
						|
            ? _.isUndefined(price)
 | 
						|
                ? '--'
 | 
						|
                : utils.getUsdValueFormattedAmount(amount, decimals, price)
 | 
						|
            : '$0.00';
 | 
						|
        return (
 | 
						|
            <PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}>
 | 
						|
                <Text fontSize="14px" fontColor={colors.darkGrey} lineHeight="1em">
 | 
						|
                    {result}
 | 
						|
                </Text>
 | 
						|
            </PlaceHolder>
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _renderWrappedEtherButton(wrappedEtherDirection: Side): React.ReactNode {
 | 
						|
        const isWrappedEtherDirectionOpen = this.state.wrappedEtherDirection === wrappedEtherDirection;
 | 
						|
        let buttonLabel;
 | 
						|
        let buttonIconName;
 | 
						|
        if (isWrappedEtherDirectionOpen) {
 | 
						|
            buttonLabel = 'cancel';
 | 
						|
            buttonIconName = 'zmdi-close';
 | 
						|
        } else {
 | 
						|
            switch (wrappedEtherDirection) {
 | 
						|
                case Side.Deposit:
 | 
						|
                    buttonLabel = 'wrap';
 | 
						|
                    buttonIconName = 'zmdi-long-arrow-down';
 | 
						|
                    break;
 | 
						|
                case Side.Receive:
 | 
						|
                    buttonLabel = 'unwrap';
 | 
						|
                    buttonIconName = 'zmdi-long-arrow-up';
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    throw errorUtils.spawnSwitchErr('wrappedEtherDirection', wrappedEtherDirection);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        const onClick = isWrappedEtherDirectionOpen
 | 
						|
            ? this._closeWrappedEtherActionRow.bind(this, wrappedEtherDirection)
 | 
						|
            : this._openWrappedEtherActionRow.bind(this, wrappedEtherDirection);
 | 
						|
        return (
 | 
						|
            <IconButton iconName={buttonIconName} labelText={buttonLabel} onClick={onClick} color={colors.mediumBlue} />
 | 
						|
        );
 | 
						|
    }
 | 
						|
    private _openWrappedEtherActionRow(wrappedEtherDirection: Side): void {
 | 
						|
        const action =
 | 
						|
            wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Opened' : 'Wallet - Unwrap WETH Opened';
 | 
						|
        analytics.track(action);
 | 
						|
        this.setState({
 | 
						|
            wrappedEtherDirection,
 | 
						|
        });
 | 
						|
    }
 | 
						|
    private _closeWrappedEtherActionRow(wrappedEtherDirection: Side): void {
 | 
						|
        const action =
 | 
						|
            wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Closed' : 'Wallet - Unwrap WETH Closed';
 | 
						|
        analytics.track(action);
 | 
						|
        this.setState({
 | 
						|
            wrappedEtherDirection: undefined,
 | 
						|
        });
 | 
						|
    }
 | 
						|
    private _getEthToken(): Token {
 | 
						|
        return utils.getEthToken(this.props.tokenByAddress);
 | 
						|
    }
 | 
						|
    private _isBlockchainReady(): boolean {
 | 
						|
        return this.props.blockchainIsLoaded && !_.isUndefined(this.props.blockchain);
 | 
						|
    }
 | 
						|
    private _getAccountState(): AccountState {
 | 
						|
        return utils.getAccountState(
 | 
						|
            this._isBlockchainReady(),
 | 
						|
            this.props.providerType,
 | 
						|
            this.props.injectedProviderName,
 | 
						|
            this.props.userAddress,
 | 
						|
        );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// tslint:disable:max-file-line-count
 |