diff options
Diffstat (limited to 'packages/website/ts/components/fill_order.tsx')
-rw-r--r-- | packages/website/ts/components/fill_order.tsx | 1270 |
1 files changed, 635 insertions, 635 deletions
diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 249ee419e..1a150e9ee 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -26,665 +26,665 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; interface FillOrderProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - orderFillAmount: BigNumber; - isOrderInUrl: boolean; - networkId: number; - userAddress: string; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; - initialOrder: Order; - dispatcher: Dispatcher; + blockchain: Blockchain; + blockchainErr: BlockchainErrs; + orderFillAmount: BigNumber; + isOrderInUrl: boolean; + networkId: number; + userAddress: string; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + initialOrder: Order; + dispatcher: Dispatcher; } interface FillOrderState { - didOrderValidationRun: boolean; - areAllInvolvedTokensTracked: boolean; - globalErrMsg: string; - orderJSON: string; - orderJSONErrMsg: string; - parsedOrder: Order; - didFillOrderSucceed: boolean; - didCancelOrderSucceed: boolean; - unavailableTakerAmount: BigNumber; - isMakerTokenAddressInRegistry: boolean; - isTakerTokenAddressInRegistry: boolean; - isFillWarningDialogOpen: boolean; - isFilling: boolean; - isCancelling: boolean; - isConfirmingTokenTracking: boolean; - tokensToTrack: Token[]; + didOrderValidationRun: boolean; + areAllInvolvedTokensTracked: boolean; + globalErrMsg: string; + orderJSON: string; + orderJSONErrMsg: string; + parsedOrder: Order; + 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> { - private _validator: SchemaValidator; - constructor(props: FillOrderProps) { - super(props); - 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: [], - }; - this._validator = new SchemaValidator(); - } - public componentWillMount() { - if (!_.isEmpty(this.state.orderJSON)) { - // tslint:disable-next-line:no-floating-promises - this._validateFillOrderFireAndForgetAsync(this.state.orderJSON); - } - } - public componentDidMount() { - window.scrollTo(0, 0); - } - public render() { - return ( - <div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}> - <h3>Fill an order</h3> - <Divider /> - <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} - networkId={this.props.networkId} - 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} - networkId={this.props.networkId} - 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() { - 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() { - const takerTokenAddress = this.state.parsedOrder.taker.token.address; - const takerToken = this.props.tokenByAddress[takerTokenAddress]; - const orderTakerAmount = new BigNumber(this.state.parsedOrder.taker.amount); - const orderMakerAmount = new BigNumber(this.state.parsedOrder.maker.amount); - const takerAssetToken = { - amount: orderTakerAmount.minus(this.state.unavailableTakerAmount), - symbol: takerToken.symbol, - }; - const fillToken = this.props.tokenByAddress[takerToken.address]; - const fillTokenState = this.props.tokenStateByAddress[takerToken.address]; - const makerTokenAddress = this.state.parsedOrder.maker.token.address; - 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 orderTaker = !_.isEmpty(this.state.parsedOrder.taker.address) - ? this.state.parsedOrder.taker.address - : this.props.userAddress; - const parsedOrderExpiration = new BigNumber(this.state.parsedOrder.expiration); - const exchangeRate = orderMakerAmount.div(orderTakerAmount); + private _validator: SchemaValidator; + constructor(props: FillOrderProps) { + super(props); + 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: [], + }; + this._validator = new SchemaValidator(); + } + public componentWillMount() { + if (!_.isEmpty(this.state.orderJSON)) { + // tslint:disable-next-line:no-floating-promises + this._validateFillOrderFireAndForgetAsync(this.state.orderJSON); + } + } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + return ( + <div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}> + <h3>Fill an order</h3> + <Divider /> + <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} + networkId={this.props.networkId} + 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} + networkId={this.props.networkId} + 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() { + 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() { + const takerTokenAddress = this.state.parsedOrder.taker.token.address; + const takerToken = this.props.tokenByAddress[takerTokenAddress]; + const orderTakerAmount = new BigNumber(this.state.parsedOrder.taker.amount); + const orderMakerAmount = new BigNumber(this.state.parsedOrder.maker.amount); + const takerAssetToken = { + amount: orderTakerAmount.minus(this.state.unavailableTakerAmount), + symbol: takerToken.symbol, + }; + const fillToken = this.props.tokenByAddress[takerToken.address]; + const fillTokenState = this.props.tokenStateByAddress[takerToken.address]; + const makerTokenAddress = this.state.parsedOrder.maker.token.address; + 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 orderTaker = !_.isEmpty(this.state.parsedOrder.taker.address) + ? this.state.parsedOrder.taker.address + : this.props.userAddress; + const parsedOrderExpiration = new BigNumber(this.state.parsedOrder.expiration); + const exchangeRate = orderMakerAmount.div(orderTakerAmount); - let orderReceiveAmount = 0; - if (!_.isUndefined(this.props.orderFillAmount)) { - const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount); - orderReceiveAmount = this._formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); - } - const isUserMaker = - !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === 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.maker.address} diameter={23} /> - </div> - <div className="col col-6"> - <EthereumAddress - address={this.state.parsedOrder.maker.address} - 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 - orderTakerAddress={orderTaker} - orderMakerAddress={this.state.parsedOrder.maker.address} - 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 - label="Fill amount" - onChange={this._onFillAmountChange.bind(this)} - shouldShowIncompleteErrs={false} - token={fillToken} - tokenState={fillTokenState} - amount={fillAssetToken.amount} - shouldCheckBalance={true} - shouldCheckAllowance={true} - /> - <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() { - return ( - <div> - Order successfully filled. See the trade details in your{' '} - <Link to={`${WebsitePaths.Portal}/trades`} style={{ color: colors.white }}> - trade history - </Link> - </div> - ); - } - private _renderCancelSuccessMsg() { - return <div>Order successfully cancelled.</div>; - } - private _onFillOrderClick() { - 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) { - this.setState({ - isFillWarningDialogOpen: false, - }); - if (!didUserCancel) { - // tslint:disable-next-line:no-floating-promises - this._onFillOrderClickFireAndForgetAsync(); - } - } - private _onFillAmountChange(isValid: boolean, amount?: BigNumber) { - this.props.dispatcher.updateOrderFillAmount(amount); - } - private _onFillOrderJSONChanged(event: any) { - 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 _checkForUntrackedTokensAndAskToAdd() { - if (!_.isEmpty(this.state.orderJSONErrMsg)) { - return; - } + let orderReceiveAmount = 0; + if (!_.isUndefined(this.props.orderFillAmount)) { + const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount); + orderReceiveAmount = this._formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); + } + const isUserMaker = + !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === 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.maker.address} diameter={23} /> + </div> + <div className="col col-6"> + <EthereumAddress + address={this.state.parsedOrder.maker.address} + 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 + orderTakerAddress={orderTaker} + orderMakerAddress={this.state.parsedOrder.maker.address} + 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 + label="Fill amount" + onChange={this._onFillAmountChange.bind(this)} + shouldShowIncompleteErrs={false} + token={fillToken} + tokenState={fillTokenState} + amount={fillAssetToken.amount} + shouldCheckBalance={true} + shouldCheckAllowance={true} + /> + <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() { + return ( + <div> + Order successfully filled. See the trade details in your{' '} + <Link to={`${WebsitePaths.Portal}/trades`} style={{ color: colors.white }}> + trade history + </Link> + </div> + ); + } + private _renderCancelSuccessMsg() { + return <div>Order successfully cancelled.</div>; + } + private _onFillOrderClick() { + 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) { + this.setState({ + isFillWarningDialogOpen: false, + }); + if (!didUserCancel) { + // tslint:disable-next-line:no-floating-promises + this._onFillOrderClickFireAndForgetAsync(); + } + } + private _onFillAmountChange(isValid: boolean, amount?: BigNumber) { + this.props.dispatcher.updateOrderFillAmount(amount); + } + private _onFillOrderJSONChanged(event: any) { + 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 _checkForUntrackedTokensAndAskToAdd() { + if (!_.isEmpty(this.state.orderJSONErrMsg)) { + return; + } - const makerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.maker.token.address]; - const takerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.taker.token.address]; + const makerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.maker.token.address]; + const takerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.taker.token.address]; - const tokensToTrack = []; - const isUnseenMakerToken = _.isUndefined(makerTokenIfExists); - const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && makerTokenIfExists.isTracked; - if (isUnseenMakerToken) { - tokensToTrack.push({ - ...this.state.parsedOrder.maker.token, - iconUrl: undefined, - isTracked: false, - isRegistered: false, - }); - } else if (!isMakerTokenTracked) { - tokensToTrack.push(makerTokenIfExists); - } - const isUnseenTakerToken = _.isUndefined(takerTokenIfExists); - const isTakerTokenTracked = !_.isUndefined(takerTokenIfExists) && takerTokenIfExists.isTracked; - if (isUnseenTakerToken) { - tokensToTrack.push({ - ...this.state.parsedOrder.taker.token, - iconUrl: undefined, - isTracked: false, - 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) { - let orderJSONErrMsg = ''; - let parsedOrder: Order; - try { - const order = JSON.parse(orderJSON); - const validationResult = this._validator.validate(order, orderSchema); - if (validationResult.errors.length > 0) { - orderJSONErrMsg = 'Submitted order JSON is not a valid order'; - utils.consoleLog(`Unexpected order JSON validation error: ${validationResult.errors.join(', ')}`); - return; - } - parsedOrder = order; + const tokensToTrack = []; + const isUnseenMakerToken = _.isUndefined(makerTokenIfExists); + const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && makerTokenIfExists.isTracked; + if (isUnseenMakerToken) { + tokensToTrack.push({ + ...this.state.parsedOrder.maker.token, + iconUrl: undefined, + isTracked: false, + isRegistered: false, + }); + } else if (!isMakerTokenTracked) { + tokensToTrack.push(makerTokenIfExists); + } + const isUnseenTakerToken = _.isUndefined(takerTokenIfExists); + const isTakerTokenTracked = !_.isUndefined(takerTokenIfExists) && takerTokenIfExists.isTracked; + if (isUnseenTakerToken) { + tokensToTrack.push({ + ...this.state.parsedOrder.taker.token, + iconUrl: undefined, + isTracked: false, + 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) { + let orderJSONErrMsg = ''; + let parsedOrder: Order; + try { + const order = JSON.parse(orderJSON); + const validationResult = this._validator.validate(order, orderSchema); + if (validationResult.errors.length > 0) { + orderJSONErrMsg = 'Submitted order JSON is not a valid order'; + utils.consoleLog(`Unexpected order JSON validation error: ${validationResult.errors.join(', ')}`); + return; + } + parsedOrder = order; - const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); - const makerAmount = new BigNumber(parsedOrder.maker.amount); - const takerAmount = new BigNumber(parsedOrder.taker.amount); - const expiration = new BigNumber(parsedOrder.expiration); - const salt = new BigNumber(parsedOrder.salt); - const parsedMakerFee = new BigNumber(parsedOrder.maker.feeAmount); - const parsedTakerFee = new BigNumber(parsedOrder.taker.feeAmount); + const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); + const makerAmount = new BigNumber(parsedOrder.maker.amount); + const takerAmount = new BigNumber(parsedOrder.taker.amount); + const expiration = new BigNumber(parsedOrder.expiration); + const salt = new BigNumber(parsedOrder.salt); + const parsedMakerFee = new BigNumber(parsedOrder.maker.feeAmount); + const parsedTakerFee = new BigNumber(parsedOrder.taker.feeAmount); - const zeroExOrder: ZeroExOrder = { - exchangeContractAddress: parsedOrder.exchangeContract, - expirationUnixTimestampSec: expiration, - feeRecipient: parsedOrder.feeRecipient, - maker: parsedOrder.maker.address, - makerFee: parsedMakerFee, - makerTokenAddress: parsedOrder.maker.token.address, - makerTokenAmount: makerAmount, - salt, - taker: _.isEmpty(parsedOrder.taker.address) ? constants.NULL_ADDRESS : parsedOrder.taker.address, - takerFee: parsedTakerFee, - takerTokenAddress: parsedOrder.taker.token.address, - takerTokenAmount: takerAmount, - }; - const orderHash = ZeroEx.getOrderHashHex(zeroExOrder); + const zeroExOrder: ZeroExOrder = { + exchangeContractAddress: parsedOrder.exchangeContract, + expirationUnixTimestampSec: expiration, + feeRecipient: parsedOrder.feeRecipient, + maker: parsedOrder.maker.address, + makerFee: parsedMakerFee, + makerTokenAddress: parsedOrder.maker.token.address, + makerTokenAmount: makerAmount, + salt, + taker: _.isEmpty(parsedOrder.taker.address) ? constants.NULL_ADDRESS : parsedOrder.taker.address, + takerFee: parsedTakerFee, + takerTokenAddress: parsedOrder.taker.token.address, + takerTokenAmount: takerAmount, + }; + const orderHash = ZeroEx.getOrderHashHex(zeroExOrder); - const signature = parsedOrder.signature; - const isValidSignature = ZeroEx.isValidSignature(signature.hash, signature, parsedOrder.maker.address); - if (this.props.networkId !== parsedOrder.networkId) { - orderJSONErrMsg = `This order was made on another Ethereum network + const signature = parsedOrder.signature; + const isValidSignature = ZeroEx.isValidSignature(signature.hash, signature, parsedOrder.maker.address); + if (this.props.networkId !== parsedOrder.networkId) { + orderJSONErrMsg = `This order was made on another Ethereum network (id: ${parsedOrder.networkId}). Connect to this network to fill.`; - parsedOrder = undefined; - } else if (exchangeContractAddr !== parsedOrder.exchangeContract) { - orderJSONErrMsg = 'This order was made using a deprecated 0x Exchange contract.'; - parsedOrder = undefined; - } else if (orderHash !== signature.hash) { - orderJSONErrMsg = 'Order hash does not match supplied plaintext values'; - parsedOrder = undefined; - } else if (!isValidSignature) { - 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) { - utils.consoleLog(`Validate order err: ${err}`); - if (!_.isEmpty(orderJSON)) { - orderJSONErrMsg = 'Submitted order JSON is not valid JSON'; - } - this.setState({ - didOrderValidationRun: true, - orderJSON, - orderJSONErrMsg, - parsedOrder, - }); - return; - } + parsedOrder = undefined; + } else if (exchangeContractAddr !== parsedOrder.exchangeContract) { + orderJSONErrMsg = 'This order was made using a deprecated 0x Exchange contract.'; + parsedOrder = undefined; + } else if (orderHash !== signature.hash) { + orderJSONErrMsg = 'Order hash does not match supplied plaintext values'; + parsedOrder = undefined; + } else if (!isValidSignature) { + 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) { + utils.consoleLog(`Validate order err: ${err}`); + if (!_.isEmpty(orderJSON)) { + orderJSONErrMsg = 'Submitted order JSON is not valid JSON'; + } + 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 { - const orderHash = parsedOrder.signature.hash; - unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); - const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( - parsedOrder.maker.token.address, - ); - const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( - parsedOrder.taker.token.address, - ); - this.setState({ - isMakerTokenAddressInRegistry, - isTakerTokenAddressInRegistry, - }); - } + let unavailableTakerAmount = new BigNumber(0); + if (!_.isEmpty(orderJSONErrMsg)) { + // Clear cache entry if user updates orderJSON to invalid entry + this.props.dispatcher.updateUserSuppliedOrderCache(undefined); + } else { + const orderHash = parsedOrder.signature.hash; + unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); + const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( + parsedOrder.maker.token.address, + ); + const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( + parsedOrder.taker.token.address, + ); + this.setState({ + isMakerTokenAddressInRegistry, + isTakerTokenAddressInRegistry, + }); + } - this.setState({ - didOrderValidationRun: true, - orderJSON, - orderJSONErrMsg, - parsedOrder, - unavailableTakerAmount, - }); + this.setState({ + didOrderValidationRun: true, + orderJSON, + orderJSONErrMsg, + parsedOrder, + unavailableTakerAmount, + }); - await this._checkForUntrackedTokensAndAskToAdd(); - } - private async _onFillOrderClickFireAndForgetAsync(): Promise<void> { - if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return; - } + await this._checkForUntrackedTokensAndAskToAdd(); + } + 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, - }); + this.setState({ + isFilling: true, + didFillOrderSucceed: false, + }); - const parsedOrder = this.state.parsedOrder; - const takerFillAmount = this.props.orderFillAmount; + 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(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'; - } + if (_.isUndefined(takerFillAmount)) { + globalErrMsg = 'You must specify a fill amount'; + } - const signedOrder = this.props.blockchain.portalOrderToSignedOrder( - parsedOrder.maker.address, - parsedOrder.taker.address, - parsedOrder.maker.token.address, - parsedOrder.taker.token.address, - new BigNumber(parsedOrder.maker.amount), - new BigNumber(parsedOrder.taker.amount), - new BigNumber(parsedOrder.maker.feeAmount), - new BigNumber(parsedOrder.taker.feeAmount), - new BigNumber(this.state.parsedOrder.expiration), - parsedOrder.feeRecipient, - parsedOrder.signature, - new BigNumber(parsedOrder.salt), - ); - if (_.isEmpty(globalErrMsg)) { - try { - await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( - signedOrder, - takerFillAmount, - this.props.userAddress, - ); - } catch (err) { - globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); - } - } - if (!_.isEmpty(globalErrMsg)) { - this.setState({ - isFilling: false, - globalErrMsg, - }); - return; - } - try { - const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync( - signedOrder, - this.props.orderFillAmount, - ); - // After fill completes, let's update the token balances - const makerToken = this.props.tokenByAddress[parsedOrder.maker.token.address]; - const takerToken = this.props.tokenByAddress[parsedOrder.taker.token.address]; - const tokens = [makerToken, takerToken]; - await this.props.blockchain.updateTokenBalancesAndAllowancesAsync(tokens); - this.setState({ - isFilling: false, - didFillOrderSucceed: true, - globalErrMsg: '', - unavailableTakerAmount: this.state.unavailableTakerAmount.plus(orderFilledAmount), - }); - return; - } catch (err) { - this.setState({ - isFilling: false, - }); - const errMsg = `${err}`; - if (_.includes(errMsg, 'User denied transaction signature')) { - return; - } - globalErrMsg = 'Failed to fill order, please refresh and try again'; - utils.consoleLog(`${err}`); - this.setState({ - globalErrMsg, - }); - await errorReporter.reportAsync(err); - return; - } - } - private async _onCancelOrderClickFireAndForgetAsync(): Promise<void> { - if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return; - } + const signedOrder = this.props.blockchain.portalOrderToSignedOrder( + parsedOrder.maker.address, + parsedOrder.taker.address, + parsedOrder.maker.token.address, + parsedOrder.taker.token.address, + new BigNumber(parsedOrder.maker.amount), + new BigNumber(parsedOrder.taker.amount), + new BigNumber(parsedOrder.maker.feeAmount), + new BigNumber(parsedOrder.taker.feeAmount), + new BigNumber(this.state.parsedOrder.expiration), + parsedOrder.feeRecipient, + parsedOrder.signature, + new BigNumber(parsedOrder.salt), + ); + if (_.isEmpty(globalErrMsg)) { + try { + await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( + signedOrder, + takerFillAmount, + this.props.userAddress, + ); + } catch (err) { + globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); + } + } + if (!_.isEmpty(globalErrMsg)) { + this.setState({ + isFilling: false, + globalErrMsg, + }); + return; + } + try { + const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync( + signedOrder, + this.props.orderFillAmount, + ); + // After fill completes, let's update the token balances + const makerToken = this.props.tokenByAddress[parsedOrder.maker.token.address]; + const takerToken = this.props.tokenByAddress[parsedOrder.taker.token.address]; + const tokens = [makerToken, takerToken]; + await this.props.blockchain.updateTokenBalancesAndAllowancesAsync(tokens); + this.setState({ + isFilling: false, + didFillOrderSucceed: true, + globalErrMsg: '', + unavailableTakerAmount: this.state.unavailableTakerAmount.plus(orderFilledAmount), + }); + return; + } catch (err) { + this.setState({ + isFilling: false, + }); + const errMsg = `${err}`; + if (_.includes(errMsg, 'User denied transaction signature')) { + return; + } + globalErrMsg = 'Failed to fill order, please refresh and try again'; + utils.consoleLog(`${err}`); + this.setState({ + globalErrMsg, + }); + await errorReporter.reportAsync(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, - }); + this.setState({ + isCancelling: true, + didCancelOrderSucceed: false, + }); - const parsedOrder = this.state.parsedOrder; - const orderHash = parsedOrder.signature.hash; - const takerAddress = this.props.userAddress; + const parsedOrder = this.state.parsedOrder; + const orderHash = parsedOrder.signature.hash; + const takerAddress = this.props.userAddress; - if (_.isUndefined(takerAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - this.setState({ - isFilling: false, - }); - return; - } - let globalErrMsg = ''; + if (_.isUndefined(takerAddress)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + this.setState({ + isFilling: false, + }); + return; + } + let globalErrMsg = ''; - const takerTokenAmount = new BigNumber(parsedOrder.taker.amount); + const takerTokenAmount = new BigNumber(parsedOrder.taker.amount); - const signedOrder = this.props.blockchain.portalOrderToSignedOrder( - parsedOrder.maker.address, - parsedOrder.taker.address, - parsedOrder.maker.token.address, - parsedOrder.taker.token.address, - new BigNumber(parsedOrder.maker.amount), - takerTokenAmount, - new BigNumber(parsedOrder.maker.feeAmount), - new BigNumber(parsedOrder.taker.feeAmount), - new BigNumber(this.state.parsedOrder.expiration), - parsedOrder.feeRecipient, - parsedOrder.signature, - new BigNumber(parsedOrder.salt), - ); - const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); - const availableTakerTokenAmount = takerTokenAmount.minus(unavailableTakerAmount); - try { - await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync(signedOrder, availableTakerTokenAmount); - } catch (err) { - globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); - } - if (!_.isEmpty(globalErrMsg)) { - this.setState({ - isCancelling: false, - globalErrMsg, - }); - return; - } - try { - await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount); - this.setState({ - isCancelling: false, - didCancelOrderSucceed: true, - globalErrMsg: '', - unavailableTakerAmount: takerTokenAmount, - }); - return; - } catch (err) { - this.setState({ - isCancelling: false, - }); - const errMsg = `${err}`; - if (_.includes(errMsg, 'User denied transaction signature')) { - return; - } - globalErrMsg = 'Failed to cancel order, please refresh and try again'; - utils.consoleLog(`${err}`); - this.setState({ - globalErrMsg, - }); - await errorReporter.reportAsync(err); - return; - } - } - private _formatCurrencyAmount(amount: BigNumber, decimals: number): number { - const unitAmount = ZeroEx.toUnitAmount(amount, decimals); - const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; - return roundedUnitAmount; - } - private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) { - if (!didConfirmTokenTracking) { - this.setState({ - orderJSON: '', - orderJSONErrMsg: '', - parsedOrder: undefined, - }); - } else { - this.setState({ - areAllInvolvedTokensTracked: true, - }); - } - this.setState({ - isConfirmingTokenTracking: !this.state.isConfirmingTokenTracking, - tokensToTrack: [], - }); - } + const signedOrder = this.props.blockchain.portalOrderToSignedOrder( + parsedOrder.maker.address, + parsedOrder.taker.address, + parsedOrder.maker.token.address, + parsedOrder.taker.token.address, + new BigNumber(parsedOrder.maker.amount), + takerTokenAmount, + new BigNumber(parsedOrder.maker.feeAmount), + new BigNumber(parsedOrder.taker.feeAmount), + new BigNumber(this.state.parsedOrder.expiration), + parsedOrder.feeRecipient, + parsedOrder.signature, + new BigNumber(parsedOrder.salt), + ); + const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); + const availableTakerTokenAmount = takerTokenAmount.minus(unavailableTakerAmount); + try { + await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync(signedOrder, availableTakerTokenAmount); + } catch (err) { + globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); + } + if (!_.isEmpty(globalErrMsg)) { + this.setState({ + isCancelling: false, + globalErrMsg, + }); + return; + } + try { + await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount); + this.setState({ + isCancelling: false, + didCancelOrderSucceed: true, + globalErrMsg: '', + unavailableTakerAmount: takerTokenAmount, + }); + return; + } catch (err) { + this.setState({ + isCancelling: false, + }); + const errMsg = `${err}`; + if (_.includes(errMsg, 'User denied transaction signature')) { + return; + } + globalErrMsg = 'Failed to cancel order, please refresh and try again'; + utils.consoleLog(`${err}`); + this.setState({ + globalErrMsg, + }); + await errorReporter.reportAsync(err); + return; + } + } + private _formatCurrencyAmount(amount: BigNumber, decimals: number): number { + const unitAmount = ZeroEx.toUnitAmount(amount, decimals); + const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; + return roundedUnitAmount; + } + private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) { + 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 |