aboutsummaryrefslogblamecommitdiffstats
path: root/packages/website/ts/components/fill_order.tsx
blob: ec73e4199342ea9908709f71e1bd18cf8fc77806 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                                 
                                         
                            
                                                              
                                          
                                                    
                               









                                                                                                     
                                                                   
                                                 
                                                                                                        
                                               
                                                        
                                                    
                                       
 







                                   
                              
                           
                                       
                          
                               







                                         
                             












                                                                                

                                                           
                                
      
                                  

                                        
                                  

















                                                                                                             
     
                                       
                                               
                                                            
                                                                            

         
                                      

                              
                                         

                                 
                                      
                                                                                           
                
                                                                      
                                                  




                                              
                     
                                                  
                             
                                                                                                     



                                                                          
                                                                
                                                                                                
                              
                                                            
                              
                      
                         



                                                                     
                          
                                                 
                                             







                                                                                                                  



                                                                                  
                                                                        
                                                                                                        


                                           
                                                            
                              
                      


                                                               
                                                                         








                                                                 
                                                                                



                  
                                                        

                 









                                                                                
                                                                                          
                  


                  
                                                   

                                                                                                                        
                                                                        

                                                                                     



                                                                              
                                                                       

                                                                                                                        








                                                                                         
                                                                                               


                                                         



                                                                
                                                                                                              
         
                           
                                                     
                                                                                       


                                                                                                   
                                                                        
                                                                    

                                                                                      


                                                       
                                                                                                                 

                                                   
                                            
                                                                                         

                                                                





                                                              








                                                                                                    
                                                                                       

                          


                                                                                                   


                                                                



                                                                          


                                                          
                                                                                              











                                                                                                  
                          
                  
                     
                                    

                                         
                                                         

                                                                                                        
                                                                                               
                              




                                                                                                            

                                         
                                                         

                                                                                                
                                                                           
                              
                                                                     
                                                                                                   



                                                                                                          
                              
                      



                      
                                                      


                                                                             
                                                                                    




                                 
                                                        
                                                        
     
                                       




                                                                                                     
                                                            
                                                       

         
                                                                



                                           
                                                            
                                                       

         
                                                                              

                                                            
                                                       




                                                                                                 
                                                        
                                                             
     
                                                                             


                                                     





                                                                                                                        
                                          
                                                                     
                                                                                                                   
                                 
                                
                                                              
                                           
                                   
                                            
                                    
               



                                                                     
                                                                                                                   
                                 
                                
                                                              
                                           
                                   
                                            
                                    
               













                                                   
                                                                                          
                                 
                                     
                              
             
                                                                 
                                                                                  

                                                                              
                                                                                                              


                                
                                                        
                                                                    
                                                                                                    
                                                    
                                                                                       
                          
                          


                                                                       
                                                                                                                   
                                        
                                           







                                                                                                
                                                       


                                                                           







                                                







                                                                           
                                                                                                           



                                                                                                                 
                                                                                                            
                                  

                                                                                                            
                                  














                                              
                                                              
     

                                                       
                                                          
     
                                                                        
                                                                                                       









                                                                              














                                                                              
                                                    


                                                                                 


                                           
                  
                           
                                                                                                                       










                                                                                            

                                           
              
                                                        

                                                                         










                                                                                                  
                                                        
                                    
                                                       


                                                                                
                                   


                             
                                      


                   
                                                                          
                                                                                                       









                                                                              









                                                                              

                                                              







                                       
                                                                      





                                                         
                                                          





                                    
                                                       

                       
                                                          
                                                                                  
                                   


                             
                                      


                   
                                                                                
                                                                      


                                                                                      
                                                                                 















                                                                             
                                       
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { colors, Link } from '@0x/react-shared';
import { BigNumber, logUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as accounting from 'accounting';
import * as _ from 'lodash';
import { Card, CardHeader, CardText } from 'material-ui/Card';
import Divider from 'material-ui/Divider';
import RaisedButton from 'material-ui/RaisedButton';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { TrackTokenConfirmationDialog } from 'ts/components/dialogs/track_token_confirmation_dialog';
import { FillOrderJSON } from 'ts/components/fill_order_json';
import { FillWarningDialog } from 'ts/components/fill_warning_dialog';
import { TokenAmountInput } from 'ts/components/inputs/token_amount_input';
import { Alert } from 'ts/components/ui/alert';
import { EthereumAddress } from 'ts/components/ui/ethereum_address';
import { Identicon } from 'ts/components/ui/identicon';
import { VisualOrder } from 'ts/components/visual_order';
import { Dispatcher } from 'ts/redux/dispatcher';
import { portalOrderSchema } from 'ts/schemas/portal_order_schema';
import { validator } from 'ts/schemas/validator';
import { AlertTypes, BlockchainErrs, PortalOrder, Token, TokenByAddress, WebsitePaths } from 'ts/types';
import { analytics } from 'ts/utils/analytics';
import { errorReporter } from 'ts/utils/error_reporter';
import { orderParser } from 'ts/utils/order_parser';
import { utils } from 'ts/utils/utils';

interface FillOrderProps {
    blockchain: Blockchain;
    blockchainErr: BlockchainErrs;
    orderFillAmount: BigNumber;
    isOrderInUrl: boolean;
    networkId: number;
    userAddress: string;
    tokenByAddress: TokenByAddress;
    initialOrder: PortalOrder;
    dispatcher: Dispatcher;
    lastForceTokenStateRefetch: number;
    isFullWidth?: boolean;
    shouldHideHeader?: boolean;
}

interface FillOrderState {
    didOrderValidationRun: boolean;
    areAllInvolvedTokensTracked: boolean;
    globalErrMsg: string;
    orderJSON: string;
    orderJSONErrMsg: string;
    parsedOrder: PortalOrder;
    didFillOrderSucceed: boolean;
    didCancelOrderSucceed: boolean;
    unavailableTakerAmount: BigNumber;
    isMakerTokenAddressInRegistry: boolean;
    isTakerTokenAddressInRegistry: boolean;
    isFillWarningDialogOpen: boolean;
    isFilling: boolean;
    isCancelling: boolean;
    isConfirmingTokenTracking: boolean;
    tokensToTrack: Token[];
}

export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
    public static defaultProps: Partial<FillOrderProps> = {
        isFullWidth: false,
        shouldHideHeader: false,
    };
    private _isUnmounted: boolean;
    constructor(props: FillOrderProps) {
        super(props);
        this._isUnmounted = false;
        this.state = {
            globalErrMsg: '',
            didOrderValidationRun: false,
            areAllInvolvedTokensTracked: false,
            didFillOrderSucceed: false,
            didCancelOrderSucceed: false,
            orderJSON: _.isUndefined(this.props.initialOrder) ? '' : JSON.stringify(this.props.initialOrder),
            orderJSONErrMsg: '',
            parsedOrder: this.props.initialOrder,
            unavailableTakerAmount: new BigNumber(0),
            isMakerTokenAddressInRegistry: false,
            isTakerTokenAddressInRegistry: false,
            isFillWarningDialogOpen: false,
            isFilling: false,
            isCancelling: false,
            isConfirmingTokenTracking: false,
            tokensToTrack: [],
        };
    }
    public componentWillMount(): void {
        if (!_.isEmpty(this.state.orderJSON)) {
            // tslint:disable-next-line:no-floating-promises
            this._validateFillOrderFireAndForgetAsync(this.state.orderJSON);
        }
    }
    public componentDidMount(): void {
        window.scrollTo(0, 0);
    }
    public componentWillUnmount(): void {
        this._isUnmounted = true;
    }
    public render(): React.ReactNode {
        const rootClassName = this.props.isFullWidth ? 'clearfix' : 'lg-px4 md-px4 sm-px2';
        return (
            <div className={rootClassName} style={{ minHeight: 600 }}>
                {!this.props.shouldHideHeader && (
                    <div>
                        <h3>Fill an order</h3>
                        <Divider />
                    </div>
                )}
                <div>
                    {!this.props.isOrderInUrl && (
                        <div>
                            <div className="pt2 pb2">Paste an order JSON snippet below to begin</div>
                            <div className="pb2">Order JSON</div>
                            <FillOrderJSON
                                blockchain={this.props.blockchain}
                                tokenByAddress={this.props.tokenByAddress}
                                orderJSON={this.state.orderJSON}
                                onFillOrderJSONChanged={this._onFillOrderJSONChanged.bind(this)}
                            />
                            {this._renderOrderJsonNotices()}
                        </div>
                    )}
                    <div>
                        {!_.isUndefined(this.state.parsedOrder) &&
                            this.state.didOrderValidationRun &&
                            this.state.areAllInvolvedTokensTracked &&
                            this._renderVisualOrder()}
                    </div>
                    {this.props.isOrderInUrl && (
                        <div className="pt2">
                            <Card
                                style={{
                                    boxShadow: 'none',
                                    backgroundColor: 'none',
                                    border: '1px solid #eceaea',
                                }}
                            >
                                <CardHeader title="Order JSON" actAsExpander={true} showExpandableButton={true} />
                                <CardText expandable={true}>
                                    <FillOrderJSON
                                        blockchain={this.props.blockchain}
                                        tokenByAddress={this.props.tokenByAddress}
                                        orderJSON={this.state.orderJSON}
                                        onFillOrderJSONChanged={this._onFillOrderJSONChanged.bind(this)}
                                    />
                                </CardText>
                            </Card>
                            {this._renderOrderJsonNotices()}
                        </div>
                    )}
                </div>
                <FillWarningDialog
                    isOpen={this.state.isFillWarningDialogOpen}
                    onToggleDialog={this._onFillWarningClosed.bind(this)}
                />
                <TrackTokenConfirmationDialog
                    userAddress={this.props.userAddress}
                    networkId={this.props.networkId}
                    blockchain={this.props.blockchain}
                    tokenByAddress={this.props.tokenByAddress}
                    dispatcher={this.props.dispatcher}
                    tokens={this.state.tokensToTrack}
                    isOpen={this.state.isConfirmingTokenTracking}
                    onToggleDialog={this._onToggleTrackConfirmDialog.bind(this)}
                />
            </div>
        );
    }
    private _renderOrderJsonNotices(): React.ReactNode {
        return (
            <div>
                {!_.isUndefined(this.props.initialOrder) &&
                    !this.state.didOrderValidationRun && (
                        <div className="pt2">
                            <span className="pr1">
                                <i className="zmdi zmdi-spinner zmdi-hc-spin" />
                            </span>
                            <span>Validating order...</span>
                        </div>
                    )}
                {!_.isEmpty(this.state.orderJSONErrMsg) && (
                    <Alert type={AlertTypes.ERROR} message={this.state.orderJSONErrMsg} />
                )}
            </div>
        );
    }
    private _renderVisualOrder(): React.ReactNode {
        const takerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.takerAssetData)
            .tokenAddress;
        const takerToken = this.props.tokenByAddress[takerTokenAddress];
        const orderTakerAmount = this.state.parsedOrder.signedOrder.takerAssetAmount;
        const orderMakerAmount = this.state.parsedOrder.signedOrder.makerAssetAmount;
        const takerAssetToken = {
            amount: orderTakerAmount.minus(this.state.unavailableTakerAmount),
            symbol: takerToken.symbol,
        };
        const fillToken = this.props.tokenByAddress[takerTokenAddress];
        const makerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.makerAssetData)
            .tokenAddress;
        const makerToken = this.props.tokenByAddress[makerTokenAddress];
        const makerAssetToken = {
            amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount),
            symbol: makerToken.symbol,
        };
        const fillAssetToken = {
            amount: this.props.orderFillAmount,
            symbol: takerToken.symbol,
        };
        const parsedOrderExpiration = this.state.parsedOrder.signedOrder.expirationTimeSeconds;

        let orderReceiveAmount = 0;
        if (!_.isUndefined(this.props.orderFillAmount)) {
            const orderReceiveAmountBigNumber = orderMakerAmount
                .times(this.props.orderFillAmount)
                .dividedBy(orderTakerAmount)
                .floor();
            orderReceiveAmount = this._formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals);
        }
        const isUserMaker =
            !_.isUndefined(this.state.parsedOrder) &&
            this.state.parsedOrder.signedOrder.makerAddress === this.props.userAddress;
        const expiryDate = utils.convertToReadableDateTimeFromUnixTimestamp(parsedOrderExpiration);
        return (
            <div className="pt3 pb1">
                <div className="clearfix pb2" style={{ width: '100%' }}>
                    <div className="inline left">Order details</div>
                    <div className="inline right" style={{ minWidth: 208 }}>
                        <div className="col col-4 pl2" style={{ color: colors.grey }}>
                            Maker:
                        </div>
                        <div className="col col-2 pr1">
                            <Identicon address={this.state.parsedOrder.signedOrder.makerAddress} diameter={23} />
                        </div>
                        <div className="col col-6">
                            <EthereumAddress
                                address={this.state.parsedOrder.signedOrder.makerAddress}
                                networkId={this.props.networkId}
                            />
                        </div>
                    </div>
                </div>
                <div className="lg-px4 md-px4 sm-px0">
                    <div className="lg-px4 md-px4 sm-px1 pt1">
                        <VisualOrder
                            makerAssetToken={makerAssetToken}
                            takerAssetToken={takerAssetToken}
                            tokenByAddress={this.props.tokenByAddress}
                            makerToken={makerToken}
                            takerToken={takerToken}
                            networkId={this.props.networkId}
                            isMakerTokenAddressInRegistry={this.state.isMakerTokenAddressInRegistry}
                            isTakerTokenAddressInRegistry={this.state.isTakerTokenAddressInRegistry}
                        />
                        <div className="center pt3 pb2">Expires: {expiryDate} UTC</div>
                    </div>
                </div>
                {!isUserMaker && (
                    <div className="clearfix mx-auto relative" style={{ width: 235, height: 108 }}>
                        <TokenAmountInput
                            blockchain={this.props.blockchain}
                            userAddress={this.props.userAddress}
                            networkId={this.props.networkId}
                            label="Fill amount"
                            onChange={this._onFillAmountChange.bind(this)}
                            shouldShowIncompleteErrs={false}
                            token={fillToken}
                            amount={fillAssetToken.amount}
                            shouldCheckBalance={true}
                            shouldCheckAllowance={true}
                            lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
                        />
                        <div
                            className="absolute sm-hide xs-hide"
                            style={{
                                color: colors.grey400,
                                right: -247,
                                top: 39,
                                width: 242,
                            }}
                        >
                            = {accounting.formatNumber(orderReceiveAmount, 6)} {makerToken.symbol}
                        </div>
                    </div>
                )}
                <div>
                    {isUserMaker ? (
                        <div>
                            <RaisedButton
                                style={{ width: '100%' }}
                                disabled={this.state.isCancelling}
                                label={this.state.isCancelling ? 'Cancelling order...' : 'Cancel order'}
                                onClick={this._onCancelOrderClickFireAndForgetAsync.bind(this)}
                            />
                            {this.state.didCancelOrderSucceed && (
                                <Alert type={AlertTypes.SUCCESS} message={this._renderCancelSuccessMsg()} />
                            )}
                        </div>
                    ) : (
                        <div>
                            <RaisedButton
                                style={{ width: '100%' }}
                                disabled={this.state.isFilling}
                                label={this.state.isFilling ? 'Filling order...' : 'Fill order'}
                                onClick={this._onFillOrderClick.bind(this)}
                            />
                            {!_.isEmpty(this.state.globalErrMsg) && (
                                <Alert type={AlertTypes.ERROR} message={this.state.globalErrMsg} />
                            )}
                            {this.state.didFillOrderSucceed && (
                                <Alert type={AlertTypes.SUCCESS} message={this._renderFillSuccessMsg()} />
                            )}
                        </div>
                    )}
                </div>
            </div>
        );
    }
    private _renderFillSuccessMsg(): React.ReactNode {
        return (
            <div>
                Order successfully filled. See the trade details in your{' '}
                <Link to={`${WebsitePaths.Portal}/trades`} fontColor={colors.white}>
                    trade history
                </Link>
            </div>
        );
    }
    private _renderCancelSuccessMsg(): React.ReactNode {
        return <div>Order successfully cancelled.</div>;
    }
    private _onFillOrderClick(): void {
        if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) {
            this.setState({
                isFillWarningDialogOpen: true,
            });
        } else {
            // tslint:disable-next-line:no-floating-promises
            this._onFillOrderClickFireAndForgetAsync();
        }
    }
    private _onFillWarningClosed(didUserCancel: boolean): void {
        this.setState({
            isFillWarningDialogOpen: false,
        });
        if (!didUserCancel) {
            // tslint:disable-next-line:no-floating-promises
            this._onFillOrderClickFireAndForgetAsync();
        }
    }
    private _onFillAmountChange(_isValid: boolean, amount?: BigNumber): void {
        this.props.dispatcher.updateOrderFillAmount(amount);
    }
    private _onFillOrderJSONChanged(event: any): void {
        const orderJSON = event.target.value;
        this.setState({
            didOrderValidationRun: _.isEmpty(orderJSON) && _.isEmpty(this.state.orderJSONErrMsg),
            didFillOrderSucceed: false,
        });
        // tslint:disable-next-line:no-floating-promises
        this._validateFillOrderFireAndForgetAsync(orderJSON);
    }
    private async _checkForUntrackedTokensAndAskToAddAsync(): Promise<void> {
        if (!_.isEmpty(this.state.orderJSONErrMsg)) {
            return;
        }
        const makerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.makerAssetData)
            .tokenAddress;
        const takerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.takerAssetData)
            .tokenAddress;
        const makerTokenIfExists = this.props.tokenByAddress[makerTokenAddress];
        const takerTokenIfExists = this.props.tokenByAddress[takerTokenAddress];
        const tokensToTrack: Token[] = [];
        const isUnseenMakerToken = _.isUndefined(makerTokenIfExists);
        const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && utils.isTokenTracked(makerTokenIfExists);
        if (isUnseenMakerToken) {
            tokensToTrack.push({
                ...this.state.parsedOrder.metadata.makerToken,
                address: makerTokenAddress,
                iconUrl: undefined,
                trackedTimestamp: undefined,
                isRegistered: false,
            });
        } else if (!isMakerTokenTracked) {
            tokensToTrack.push(makerTokenIfExists);
        }
        const isUnseenTakerToken = _.isUndefined(takerTokenIfExists);
        const isTakerTokenTracked = !_.isUndefined(takerTokenIfExists) && utils.isTokenTracked(takerTokenIfExists);
        if (isUnseenTakerToken) {
            tokensToTrack.push({
                ...this.state.parsedOrder.metadata.takerToken,
                address: takerTokenAddress,
                iconUrl: undefined,
                trackedTimestamp: undefined,
                isRegistered: false,
            });
        } else if (!isTakerTokenTracked) {
            tokensToTrack.push(takerTokenIfExists);
        }
        if (!_.isEmpty(tokensToTrack)) {
            this.setState({
                isConfirmingTokenTracking: true,
                tokensToTrack,
            });
        } else {
            this.setState({
                areAllInvolvedTokensTracked: true,
            });
        }
    }
    private async _validateFillOrderFireAndForgetAsync(orderJSON: string): Promise<void> {
        let orderJSONErrMsg = '';
        let parsedOrder: PortalOrder;
        let orderHash: string;
        try {
            const order = orderParser.parseJsonString(orderJSON);
            const validationResult = validator.validate(order, portalOrderSchema);
            if (validationResult.errors.length > 0) {
                orderJSONErrMsg = 'Submitted order JSON is not a valid order';
                logUtils.log(`Unexpected order JSON validation error: ${validationResult.errors.join(', ')}`);
                return;
            }
            parsedOrder = order;
            const signedOrder = parsedOrder.signedOrder;
            orderHash = orderHashUtils.getOrderHashHex(signedOrder);
            const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists();
            const signature = signedOrder.signature;
            const isSignatureValid = await this.props.blockchain.isValidSignatureAsync(
                orderHash,
                signature,
                signedOrder.makerAddress,
            );
            if (exchangeContractAddr !== signedOrder.exchangeAddress) {
                orderJSONErrMsg = 'This order was made on another network or using a deprecated Exchange contract';
                parsedOrder = undefined;
            } else if (!isSignatureValid) {
                orderJSONErrMsg = 'Order signature is invalid';
                parsedOrder = undefined;
            } else {
                // Update user supplied order cache so that if they navigate away from fill view
                // e.g to set a token allowance, when they come back, the fill order persists
                this.props.dispatcher.updateUserSuppliedOrderCache(parsedOrder);
            }
        } catch (err) {
            logUtils.log(`Validate order err: ${err}`);
            if (!_.isEmpty(orderJSON)) {
                orderJSONErrMsg = 'Submitted order JSON is not valid JSON';
            }
            if (!this._isUnmounted) {
                this.setState({
                    didOrderValidationRun: true,
                    orderJSON,
                    orderJSONErrMsg,
                    parsedOrder,
                });
            }
            return;
        }

        let unavailableTakerAmount = new BigNumber(0);
        if (!_.isEmpty(orderJSONErrMsg)) {
            // Clear cache entry if user updates orderJSON to invalid entry
            this.props.dispatcher.updateUserSuppliedOrderCache(undefined);
        } else {
            unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash);
            const makerTokenAddress = assetDataUtils.decodeERC20AssetData(parsedOrder.signedOrder.makerAssetData)
                .tokenAddress;
            const takerTokenAddress = assetDataUtils.decodeERC20AssetData(parsedOrder.signedOrder.takerAssetData)
                .tokenAddress;
            const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync(
                makerTokenAddress,
            );
            const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync(
                takerTokenAddress,
            );
            this.setState({
                isMakerTokenAddressInRegistry,
                isTakerTokenAddressInRegistry,
            });
        }

        this.setState({
            didOrderValidationRun: true,
            orderJSON,
            orderJSONErrMsg,
            parsedOrder,
            unavailableTakerAmount,
        });

        await this._checkForUntrackedTokensAndAskToAddAsync();
    }
    private _trackOrderEvent(eventName: string): void {
        const parsedOrder = this.state.parsedOrder;
        analytics.trackOrderEvent(eventName, parsedOrder);
    }
    private async _onFillOrderClickFireAndForgetAsync(): Promise<void> {
        if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
            this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
            return;
        }

        this.setState({
            isFilling: true,
            didFillOrderSucceed: false,
        });

        const parsedOrder = this.state.parsedOrder;
        const takerFillAmount = this.props.orderFillAmount;

        if (_.isUndefined(this.props.userAddress)) {
            this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
            this.setState({
                isFilling: false,
            });
            return;
        }
        let globalErrMsg = '';

        if (_.isUndefined(takerFillAmount)) {
            globalErrMsg = 'You must specify a fill amount';
        }

        const signedOrder = parsedOrder.signedOrder;
        if (_.isEmpty(globalErrMsg)) {
            try {
                await this.props.blockchain.validateFillOrderThrowIfInvalidAsync(
                    signedOrder,
                    takerFillAmount,
                    this.props.userAddress,
                );
            } catch (err) {
                globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.signedOrder.takerAddress);
            }
        }
        if (!_.isEmpty(globalErrMsg)) {
            this.setState({
                isFilling: false,
                globalErrMsg,
            });
            return;
        }
        try {
            const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync(
                signedOrder,
                this.props.orderFillAmount,
            );
            this._trackOrderEvent('Fill Order Success');
            // After fill completes, let's force fetch the token balances
            this.props.dispatcher.forceTokenStateRefetch();
            this.setState({
                isFilling: false,
                didFillOrderSucceed: true,
                globalErrMsg: '',
                unavailableTakerAmount: this.state.unavailableTakerAmount.plus(orderFilledAmount),
            });
            return;
        } catch (err) {
            this.setState({
                isFilling: false,
            });
            this._trackOrderEvent('Fill Order Failure');
            const errMsg = `${err}`;
            if (utils.didUserDenyWeb3Request(errMsg)) {
                return;
            }
            globalErrMsg = 'Failed to fill order, please refresh and try again';
            logUtils.log(`${err}`);
            this.setState({
                globalErrMsg,
            });
            errorReporter.report(err);
            return;
        }
    }
    private async _onCancelOrderClickFireAndForgetAsync(): Promise<void> {
        if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
            this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
            return;
        }

        this.setState({
            isCancelling: true,
            didCancelOrderSucceed: false,
        });

        const parsedOrder = this.state.parsedOrder;
        const takerAddress = this.props.userAddress;

        if (_.isUndefined(takerAddress)) {
            this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
            this.setState({
                isFilling: false,
            });
            return;
        }
        let globalErrMsg = '';
        const signedOrder = parsedOrder.signedOrder;
        const takerTokenAmount = signedOrder.takerAssetAmount;
        if (!_.isEmpty(globalErrMsg)) {
            this.setState({
                isCancelling: false,
                globalErrMsg,
            });
            return;
        }
        try {
            await this.props.blockchain.cancelOrderAsync(signedOrder);
            this.setState({
                isCancelling: false,
                didCancelOrderSucceed: true,
                globalErrMsg: '',
                unavailableTakerAmount: takerTokenAmount,
            });
            this._trackOrderEvent('Cancel Order Success');
            return;
        } catch (err) {
            this.setState({
                isCancelling: false,
            });
            const errMsg = `${err}`;
            if (utils.didUserDenyWeb3Request(errMsg)) {
                return;
            }
            this._trackOrderEvent('Cancel Order Failure');
            globalErrMsg = 'Failed to cancel order, please refresh and try again';
            logUtils.log(`${err}`);
            this.setState({
                globalErrMsg,
            });
            errorReporter.report(err);
            return;
        }
    }
    private _formatCurrencyAmount(amount: BigNumber, decimals: number): number {
        const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
        const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000;
        return roundedUnitAmount;
    }
    private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean): void {
        if (!didConfirmTokenTracking) {
            this.setState({
                orderJSON: '',
                orderJSONErrMsg: '',
                parsedOrder: undefined,
            });
        } else {
            this.setState({
                areAllInvolvedTokensTracked: true,
            });
        }
        this.setState({
            isConfirmingTokenTracking: !this.state.isConfirmingTokenTracking,
            tokensToTrack: [],
        });
    }
} // tslint:disable:max-file-line-count