diff options
author | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
---|---|---|
committer | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
commit | 35703539d0f2b4ddb3b11d0de8c9634af59ab71f (patch) | |
tree | ae3731221dbbb3a6fa40060a8d916cfd3f738289 /packages/website/ts/components/generate_order | |
parent | 92a1fde5b1ecd81b07cdb5bf0c9c1cd3544799db (diff) | |
download | dexon-0x-contracts-stable.tar.gz dexon-0x-contracts-stable.tar.zst dexon-0x-contracts-stable.zip |
Deploy @dexon-foundation/0x.jsstable
Diffstat (limited to 'packages/website/ts/components/generate_order')
3 files changed, 0 insertions, 898 deletions
diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx deleted file mode 100644 index d3f11f962..000000000 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import * as _ from 'lodash'; -import Dialog from 'material-ui/Dialog'; -import FlatButton from 'material-ui/FlatButton'; -import * as moment from 'moment'; -import * as React from 'react'; -import firstBy from 'thenby'; - -import { Blockchain } from 'ts/blockchain'; -import { NewTokenForm } from 'ts/components/generate_order/new_token_form'; -import { TrackTokenConfirmation } from 'ts/components/track_token_confirmation'; -import { TokenIcon } from 'ts/components/ui/token_icon'; -import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { DialogConfigs, Token, TokenByAddress, TokenVisibility } from 'ts/types'; -import { constants } from 'ts/utils/constants'; -import { utils } from 'ts/utils/utils'; - -const TOKEN_ICON_DIMENSION = 100; -const TILE_DIMENSION = 146; -enum AssetViews { - AssetPicker = 'ASSET_PICKER', - NewTokenForm = 'NEW_TOKEN_FORM', - ConfirmTrackToken = 'CONFIRM_TRACK_TOKEN', -} - -interface AssetPickerProps { - userAddress: string; - blockchain: Blockchain; - dispatcher: Dispatcher; - networkId: number; - isOpen: boolean; - currentTokenAddress: string; - onTokenChosen: (tokenAddress: string) => void; - tokenByAddress: TokenByAddress; - tokenVisibility?: TokenVisibility; -} - -interface AssetPickerState { - assetView: AssetViews; - hoveredAddress: string | undefined; - chosenTrackTokenAddress: string; - isAddingTokenToTracked: boolean; -} - -export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerState> { - public static defaultProps: Partial<AssetPickerProps> = { - tokenVisibility: TokenVisibility.All, - }; - private readonly _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs }; - constructor(props: AssetPickerProps) { - super(props); - this.state = { - assetView: AssetViews.AssetPicker, - hoveredAddress: undefined, - chosenTrackTokenAddress: undefined, - isAddingTokenToTracked: false, - }; - this._dialogConfigsByAssetView = { - [AssetViews.AssetPicker]: { - title: 'Select token', - isModal: false, - actions: [], - }, - [AssetViews.NewTokenForm]: { - title: 'Add an ERC20 token', - isModal: false, - actions: [], - }, - [AssetViews.ConfirmTrackToken]: { - title: 'Tracking confirmation', - isModal: true, - actions: [ - <FlatButton - key="noTracking" - label="No" - onClick={this._onTrackConfirmationRespondedAsync.bind(this, false)} - />, - <FlatButton - key="yesTrack" - label="Yes" - onClick={this._onTrackConfirmationRespondedAsync.bind(this, true)} - />, - ], - }, - }; - } - public render(): React.ReactNode { - const dialogConfigs: DialogConfigs = this._dialogConfigsByAssetView[this.state.assetView]; - return ( - <Dialog - title={dialogConfigs.title} - modal={dialogConfigs.isModal} - open={this.props.isOpen} - actions={dialogConfigs.actions} - autoScrollBodyContent={true} - onRequestClose={this._onCloseDialog.bind(this)} - > - {this.state.assetView === AssetViews.AssetPicker && this._renderAssetPicker()} - {this.state.assetView === AssetViews.NewTokenForm && ( - <NewTokenForm - blockchain={this.props.blockchain} - onNewTokenSubmitted={this._onNewTokenSubmitted.bind(this)} - tokenByAddress={this.props.tokenByAddress} - /> - )} - {this.state.assetView === AssetViews.ConfirmTrackToken && this._renderConfirmTrackToken()} - </Dialog> - ); - } - private _renderConfirmTrackToken(): React.ReactNode { - const token = this.props.tokenByAddress[this.state.chosenTrackTokenAddress]; - return ( - <TrackTokenConfirmation - tokens={[token]} - tokenByAddress={this.props.tokenByAddress} - networkId={this.props.networkId} - isAddingTokenToTracked={this.state.isAddingTokenToTracked} - /> - ); - } - private _renderAssetPicker(): React.ReactNode { - return ( - <div - className="flex flex-wrap" - style={{ - maxWidth: 1000, - maxHeight: 600, - marginBottom: 10, - }} - > - {this._renderGridTiles()} - </div> - ); - } - private _renderGridTiles(): React.ReactNode { - let isHovered; - let tileStyles; - const allTokens = _.values(this.props.tokenByAddress); - // filter tokens based on visibility specified in props, do not show ZRX or ETHER as tracked or untracked - const filteredTokens = - this.props.tokenVisibility === TokenVisibility.All - ? allTokens - : _.filter(allTokens, token => { - return ( - token.symbol !== constants.ZRX_TOKEN_SYMBOL && - token.symbol !== constants.ETHER_TOKEN_SYMBOL && - ((this.props.tokenVisibility === TokenVisibility.Tracked && utils.isTokenTracked(token)) || - (this.props.tokenVisibility === TokenVisibility.Untracked && - !utils.isTokenTracked(token))) - ); - }); - // if we are showing tracked tokens, sort by date added, otherwise sort by symbol - const sortKey = this.props.tokenVisibility === TokenVisibility.Tracked ? 'trackedTimestamp' : 'symbol'; - const sortedTokens = filteredTokens.sort(firstBy(sortKey)); - if (_.isEmpty(sortedTokens)) { - return <div className="mx-auto p4 h2">No tokens to remove.</div>; - } - const gridTiles = _.map(sortedTokens, token => { - const address = token.address; - isHovered = this.state.hoveredAddress === address; - tileStyles = { - cursor: 'pointer', - opacity: isHovered ? 0.6 : 1, - }; - return ( - <div - key={address} - style={{ - width: TILE_DIMENSION, - height: TILE_DIMENSION, - ...tileStyles, - }} - className="p2 flex sm-col-6 md-col-3 lg-col-3 flex-column items-center mx-auto" - onClick={this._onChooseToken.bind(this, address)} - onMouseEnter={this._onToggleHover.bind(this, address, true)} - onMouseLeave={this._onToggleHover.bind(this, address, false)} - > - <div className="p1"> - <TokenIcon token={token} diameter={TOKEN_ICON_DIMENSION} /> - </div> - <div className="center">{token.symbol}</div> - </div> - ); - }); - const otherTokenKey = 'otherToken'; - isHovered = this.state.hoveredAddress === otherTokenKey; - tileStyles = { - cursor: 'pointer', - opacity: isHovered ? 0.6 : 1, - }; - if (this.props.tokenVisibility !== TokenVisibility.Tracked) { - gridTiles.push( - <div - key={otherTokenKey} - style={{ - width: TILE_DIMENSION, - height: TILE_DIMENSION, - ...tileStyles, - }} - className="p2 flex sm-col-6 md-col-3 lg-col-3 flex-column items-center mx-auto" - onClick={this._onCustomAssetChosen.bind(this)} - onMouseEnter={this._onToggleHover.bind(this, otherTokenKey, true)} - onMouseLeave={this._onToggleHover.bind(this, otherTokenKey, false)} - > - <div className="p1 center"> - <i - style={{ fontSize: 105, paddingLeft: 1, paddingRight: 1 }} - className="zmdi zmdi-plus-circle" - /> - </div> - <div className="center">Other ERC20 Token</div> - </div>, - ); - } - return gridTiles; - } - private _onToggleHover(address: string, isHovered: boolean): void { - const hoveredAddress = isHovered ? address : undefined; - this.setState({ - hoveredAddress, - }); - } - private _onCloseDialog(): void { - this.setState({ - assetView: AssetViews.AssetPicker, - }); - this.props.onTokenChosen(this.props.currentTokenAddress); - } - private _onChooseToken(tokenAddress: string): void { - const token = this.props.tokenByAddress[tokenAddress]; - if (utils.isTokenTracked(token)) { - this.props.onTokenChosen(tokenAddress); - } else { - this.setState({ - assetView: AssetViews.ConfirmTrackToken, - chosenTrackTokenAddress: tokenAddress, - }); - } - } - private _onCustomAssetChosen(): void { - this.setState({ - assetView: AssetViews.NewTokenForm, - }); - } - private _onNewTokenSubmitted(newToken: Token): void { - trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newToken); - this.props.dispatcher.addTokenToTokenByAddress(newToken); - this.setState({ - assetView: AssetViews.AssetPicker, - }); - this.props.onTokenChosen(newToken.address); - } - private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean): Promise<void> { - const resetState: AssetPickerState = { - ...this.state, - isAddingTokenToTracked: false, - assetView: AssetViews.AssetPicker, - chosenTrackTokenAddress: undefined, - }; - if (!didUserAcceptTracking) { - this.setState(resetState); - this._onCloseDialog(); - return; - } - this.setState({ - isAddingTokenToTracked: true, - }); - const tokenAddress = this.state.chosenTrackTokenAddress; - const token = this.props.tokenByAddress[tokenAddress]; - if (_.isUndefined(tokenAddress)) { - this.setState(resetState); - return; - } - const newTokenEntry = { - ...token, - trackedTimestamp: moment().unix(), - }; - trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry); - - this.props.dispatcher.updateTokenByAddress([newTokenEntry]); - this.setState(resetState); - this.props.onTokenChosen(tokenAddress); - } -} diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx deleted file mode 100644 index 0f70aa18f..000000000 --- a/packages/website/ts/components/generate_order/generate_order_form.tsx +++ /dev/null @@ -1,385 +0,0 @@ -import { assetDataUtils, generatePseudoRandomSalt, orderHashUtils } from '@0x/order-utils'; -import { colors } from '@0x/react-shared'; -import { Order as ZeroExOrder } from '@0x/types'; -import { BigNumber, logUtils } from '@0x/utils'; -import * as _ from 'lodash'; -import Dialog from 'material-ui/Dialog'; -import Divider from 'material-ui/Divider'; -import * as React from 'react'; -import { Blockchain } from 'ts/blockchain'; -import { ExpirationInput } from 'ts/components/inputs/expiration_input'; -import { HashInput } from 'ts/components/inputs/hash_input'; -import { IdenticonAddressInput } from 'ts/components/inputs/identicon_address_input'; -import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; -import { TokenInput } from 'ts/components/inputs/token_input'; -import { OrderJSON } from 'ts/components/order_json'; -import { Alert } from 'ts/components/ui/alert'; -import { HelpTooltip } from 'ts/components/ui/help_tooltip'; -import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; -import { SwapIcon } from 'ts/components/ui/swap_icon'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { portalOrderSchema } from 'ts/schemas/portal_order_schema'; -import { validator } from 'ts/schemas/validator'; -import { - AlertTypes, - BlockchainErrs, - HashData, - PortalOrder, - Side, - SideToAssetToken, - Token, - TokenByAddress, -} from 'ts/types'; -import { analytics } from 'ts/utils/analytics'; -import { constants } from 'ts/utils/constants'; -import { errorReporter } from 'ts/utils/error_reporter'; -import { utils } from 'ts/utils/utils'; - -enum SigningState { - Unsigned, - Signing, - Signed, -} - -interface GenerateOrderFormProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - dispatcher: Dispatcher; - hashData: HashData; - orderExpiryTimestamp: BigNumber; - networkId: number; - userAddress: string; - orderSignature: string; - orderTakerAddress: string; - orderSalt: BigNumber; - sideToAssetToken: SideToAssetToken; - tokenByAddress: TokenByAddress; - lastForceTokenStateRefetch: number; - isFullWidth?: boolean; - shouldHideHeader?: boolean; -} - -interface GenerateOrderFormState { - globalErrMsg: string; - shouldShowIncompleteErrs: boolean; - signingState: SigningState; -} - -export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, GenerateOrderFormState> { - public static defaultProps: Partial<GenerateOrderFormProps> = { - isFullWidth: false, - shouldHideHeader: false, - }; - constructor(props: GenerateOrderFormProps) { - super(props); - this.state = { - globalErrMsg: '', - shouldShowIncompleteErrs: false, - signingState: SigningState.Unsigned, - }; - } - public componentDidMount(): void { - window.scrollTo(0, 0); - } - public render(): React.ReactNode { - const dispatcher = this.props.dispatcher; - const depositTokenAddress = this.props.sideToAssetToken[Side.Deposit].address; - const depositToken = this.props.tokenByAddress[depositTokenAddress]; - const receiveTokenAddress = this.props.sideToAssetToken[Side.Receive].address; - const receiveToken = this.props.tokenByAddress[receiveTokenAddress]; - const takerExplanation = - 'If a taker is specified, only they are<br> \ - allowed to fill this order. If no taker is<br> \ - specified, anyone is able to fill it.'; - const exchangeContractIfExists = this.props.blockchain.getExchangeContractAddressIfExists(); - const initialTakerAddress = - this.props.orderTakerAddress === constants.NULL_ADDRESS ? '' : this.props.orderTakerAddress; - const rootClassName = this.props.isFullWidth ? 'clearfix mb2' : 'clearfix mb2 lg-px4 md-px4 sm-px2'; - return ( - <div className={rootClassName}> - {!this.props.shouldHideHeader && ( - <div> - <h3>Generate an order</h3> - <Divider /> - </div> - )} - <div className="mx-auto" style={{ maxWidth: 580 }}> - <div className="pt3"> - <div className="mx-auto clearfix"> - <div className="lg-col md-col lg-col-5 md-col-5 sm-col sm-col-5 sm-pb2"> - <TokenInput - userAddress={this.props.userAddress} - blockchain={this.props.blockchain} - blockchainErr={this.props.blockchainErr} - dispatcher={this.props.dispatcher} - label="Selling" - side={Side.Deposit} - networkId={this.props.networkId} - assetToken={this.props.sideToAssetToken[Side.Deposit]} - updateChosenAssetToken={dispatcher.updateChosenAssetToken.bind(dispatcher)} - tokenByAddress={this.props.tokenByAddress} - /> - <TokenAmountInput - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - blockchain={this.props.blockchain} - userAddress={this.props.userAddress} - networkId={this.props.networkId} - label="Sell amount" - token={depositToken} - amount={this.props.sideToAssetToken[Side.Deposit].amount} - onChange={this._onTokenAmountChange.bind(this, depositToken, Side.Deposit)} - shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs} - shouldCheckBalance={true} - shouldCheckAllowance={true} - /> - </div> - <div className="lg-col md-col lg-col-2 md-col-2 sm-col sm-col-2 xs-hide"> - <div className="p1"> - <SwapIcon swapTokensFn={dispatcher.swapAssetTokenSymbols.bind(dispatcher)} /> - </div> - </div> - <div className="lg-col md-col lg-col-5 md-col-5 sm-col sm-col-5 sm-pb2"> - <TokenInput - userAddress={this.props.userAddress} - blockchain={this.props.blockchain} - blockchainErr={this.props.blockchainErr} - dispatcher={this.props.dispatcher} - label="Buying" - side={Side.Receive} - networkId={this.props.networkId} - assetToken={this.props.sideToAssetToken[Side.Receive]} - updateChosenAssetToken={dispatcher.updateChosenAssetToken.bind(dispatcher)} - tokenByAddress={this.props.tokenByAddress} - /> - <TokenAmountInput - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - blockchain={this.props.blockchain} - userAddress={this.props.userAddress} - networkId={this.props.networkId} - label="Receive amount" - token={receiveToken} - amount={this.props.sideToAssetToken[Side.Receive].amount} - onChange={this._onTokenAmountChange.bind(this, receiveToken, Side.Receive)} - shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs} - shouldCheckBalance={false} - shouldCheckAllowance={false} - /> - </div> - </div> - </div> - <div className="pt1 sm-pb2 lg-px4 md-px4"> - <div className="lg-px3 md-px3"> - <div style={{ fontSize: 12, color: colors.grey }}>Expiration</div> - <ExpirationInput - orderExpiryTimestamp={this.props.orderExpiryTimestamp} - updateOrderExpiry={dispatcher.updateOrderExpiry.bind(dispatcher)} - /> - </div> - </div> - <div className="pt1 flex mx-auto"> - <IdenticonAddressInput - label="Taker" - initialAddress={initialTakerAddress} - updateOrderAddress={this._updateOrderAddress.bind(this)} - /> - <div className="pt3"> - <div className="pl1"> - <HelpTooltip explanation={takerExplanation} /> - </div> - </div> - </div> - <div> - <HashInput - blockchain={this.props.blockchain} - blockchainIsLoaded={this.props.blockchainIsLoaded} - hashData={this.props.hashData} - label="Order Hash" - /> - </div> - <div className="pt2"> - <div className="center"> - <LifeCycleRaisedButton - labelReady="Sign hash" - labelLoading="Signing..." - labelComplete="Hash signed!" - onClickAsyncFn={this._onSignClickedAsync.bind(this)} - /> - </div> - {this.state.globalErrMsg !== '' && ( - <Alert type={AlertTypes.Error} message={this.state.globalErrMsg} /> - )} - </div> - </div> - <Dialog - title="Order JSON" - titleStyle={{ fontWeight: 100 }} - modal={false} - open={this.state.signingState === SigningState.Signed} - onRequestClose={this._onCloseOrderJSONDialog.bind(this)} - > - <OrderJSON - exchangeContractIfExists={exchangeContractIfExists} - orderExpiryTimestamp={this.props.orderExpiryTimestamp} - orderSignature={this.props.orderSignature} - orderTakerAddress={this.props.orderTakerAddress} - orderMakerAddress={this.props.userAddress} - orderSalt={this.props.orderSalt} - orderMakerFee={this.props.hashData.makerFee} - orderTakerFee={this.props.hashData.takerFee} - orderFeeRecipient={this.props.hashData.feeRecipientAddress} - sideToAssetToken={this.props.sideToAssetToken} - tokenByAddress={this.props.tokenByAddress} - /> - </Dialog> - </div> - ); - } - private _onTokenAmountChange(token: Token, side: Side, _isValid: boolean, amount?: BigNumber): void { - this.props.dispatcher.updateChosenAssetToken(side, { - address: token.address, - amount, - }); - } - private _onCloseOrderJSONDialog(): void { - // Upon closing the order JSON dialog, we update the orderSalt stored in the Redux store - // with a new value so that if a user signs the identical order again, the newly signed - // orderHash will not collide with the previously generated orderHash. - this.props.dispatcher.updateOrderSalt(generatePseudoRandomSalt()); - this.setState({ - signingState: SigningState.Unsigned, - }); - } - private async _onSignClickedAsync(): Promise<boolean> { - if (this.props.blockchainErr !== BlockchainErrs.NoError) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } - - // Check if all required inputs were supplied - const debitToken = this.props.sideToAssetToken[Side.Deposit]; - const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; - const [debitBalance, debitAllowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - userAddressIfExists, - debitToken.address, - ); - const receiveToken = this.props.sideToAssetToken[Side.Receive]; - const receiveAmount = receiveToken.amount; - if ( - !_.isUndefined(debitToken.amount) && - !_.isUndefined(receiveAmount) && - debitToken.amount.gt(0) && - receiveAmount.gt(0) && - this.props.userAddress !== '' && - debitBalance.gte(debitToken.amount) && - debitAllowance.gte(debitToken.amount) - ) { - const signedOrder = await this._signTransactionAsync(); - const doesSignedOrderExist = !_.isUndefined(signedOrder); - if (doesSignedOrderExist) { - analytics.trackOrderEvent('Sign Order Success', signedOrder); - this.setState({ - globalErrMsg: '', - shouldShowIncompleteErrs: false, - }); - } - return doesSignedOrderExist; - } else { - let globalErrMsg = 'You must fix the above errors in order to generate a valid order'; - if (this.props.userAddress === '') { - globalErrMsg = 'You must enable wallet communication'; - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - } - analytics.track('Sign Order Failure', { - makerTokenAmount: debitToken.amount.toString(), - makerToken: this.props.tokenByAddress[debitToken.address].symbol, - takerTokenAmount: receiveToken.amount.toString(), - takerToken: this.props.tokenByAddress[receiveToken.address].symbol, - }); - this.setState({ - globalErrMsg, - shouldShowIncompleteErrs: true, - }); - return false; - } - } - private async _signTransactionAsync(): Promise<PortalOrder | undefined> { - this.setState({ - signingState: SigningState.Signing, - }); - const exchangeAddress = this.props.blockchain.getExchangeContractAddressIfExists(); - if (_.isUndefined(exchangeAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - this.setState({ - signingState: SigningState.Unsigned, - }); - return undefined; - } - const hashData = this.props.hashData; - - const makerAssetData = assetDataUtils.encodeERC20AssetData(hashData.depositTokenContractAddr); - const takerAssetData = assetDataUtils.encodeERC20AssetData(hashData.receiveTokenContractAddr); - const zeroExOrder: ZeroExOrder = { - senderAddress: constants.NULL_ADDRESS, - exchangeAddress, - expirationTimeSeconds: hashData.orderExpiryTimestamp, - feeRecipientAddress: hashData.feeRecipientAddress, - makerAddress: hashData.orderMakerAddress, - makerFee: hashData.makerFee, - makerAssetData, - makerAssetAmount: hashData.depositAmount, - salt: hashData.orderSalt, - takerAddress: hashData.orderTakerAddress, - takerFee: hashData.takerFee, - takerAssetData, - takerAssetAmount: hashData.receiveAmount, - }; - const orderHash = orderHashUtils.getOrderHashHex(zeroExOrder); - - let globalErrMsg = ''; - let order; - try { - const signature = await this.props.blockchain.signOrderHashAsync(orderHash); - order = utils.generateOrder( - exchangeAddress, - this.props.sideToAssetToken, - hashData.orderExpiryTimestamp, - this.props.orderTakerAddress, - this.props.userAddress, - hashData.makerFee, - hashData.takerFee, - hashData.feeRecipientAddress, - signature, - this.props.tokenByAddress, - hashData.orderSalt, - ); - const validationResult = validator.validate(order, portalOrderSchema); - if (validationResult.errors.length > 0) { - globalErrMsg = 'Order signing failed. Please refresh and try again'; - logUtils.log(`Unexpected error occured: Order validation failed: - ${validationResult.errors}`); - } - } catch (err) { - const errMsg = `${err}`; - if (utils.didUserDenyWeb3Request(errMsg)) { - globalErrMsg = 'User denied sign request'; - } else { - globalErrMsg = 'An unexpected error occured. Please try refreshing the page'; - logUtils.log(`Unexpected error occured: ${err}`); - logUtils.log(err.stack); - errorReporter.report(err); - } - } - this.setState({ - signingState: globalErrMsg === '' ? SigningState.Signed : SigningState.Unsigned, - globalErrMsg, - }); - return order; - } - private _updateOrderAddress(address?: string): void { - if (!_.isUndefined(address)) { - const normalizedAddress = _.isEmpty(address) ? constants.NULL_ADDRESS : address; - this.props.dispatcher.updateOrderTakerAddress(normalizedAddress); - } - } -} diff --git a/packages/website/ts/components/generate_order/new_token_form.tsx b/packages/website/ts/components/generate_order/new_token_form.tsx deleted file mode 100644 index ce684d177..000000000 --- a/packages/website/ts/components/generate_order/new_token_form.tsx +++ /dev/null @@ -1,229 +0,0 @@ -import { colors } from '@0x/react-shared'; -import * as _ from 'lodash'; -import TextField from 'material-ui/TextField'; -import * as moment from 'moment'; -import * as React from 'react'; -import { Blockchain } from 'ts/blockchain'; -import { AddressInput } from 'ts/components/inputs/address_input'; -import { Alert } from 'ts/components/ui/alert'; -import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; -import { RequiredLabel } from 'ts/components/ui/required_label'; -import { AlertTypes, Token, TokenByAddress } from 'ts/types'; - -interface NewTokenFormProps { - blockchain: Blockchain; - tokenByAddress: TokenByAddress; - onNewTokenSubmitted: (token: Token) => void; -} - -interface NewTokenFormState { - globalErrMsg: string; - name: string; - nameErrText: string; - symbol: string; - symbolErrText: string; - address: string; - shouldShowAddressIncompleteErr: boolean; - decimals: string; - decimalsErrText: string; -} - -export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFormState> { - constructor(props: NewTokenFormProps) { - super(props); - this.state = { - address: '', - globalErrMsg: '', - name: '', - nameErrText: '', - shouldShowAddressIncompleteErr: false, - symbol: '', - symbolErrText: '', - decimals: '18', - decimalsErrText: '', - }; - } - public render(): React.ReactNode { - return ( - <div className="mx-auto pb2" style={{ width: 256 }}> - <div> - <TextField - floatingLabelFixed={true} - floatingLabelStyle={{ color: colors.grey }} - floatingLabelText={<RequiredLabel label="Name" />} - value={this.state.name} - errorText={this.state.nameErrText} - onChange={this._onTokenNameChanged.bind(this)} - /> - </div> - <div> - <TextField - floatingLabelFixed={true} - floatingLabelStyle={{ color: colors.grey }} - floatingLabelText={<RequiredLabel label="Symbol" />} - value={this.state.symbol} - errorText={this.state.symbolErrText} - onChange={this._onTokenSymbolChanged.bind(this)} - /> - </div> - <div> - <AddressInput - isRequired={true} - label="Contract address" - initialAddress="" - shouldShowIncompleteErrs={this.state.shouldShowAddressIncompleteErr} - updateAddress={this._onTokenAddressChanged.bind(this)} - /> - </div> - <div> - <TextField - floatingLabelFixed={true} - floatingLabelStyle={{ color: colors.grey }} - floatingLabelText={<RequiredLabel label="Decimals" />} - value={this.state.decimals} - errorText={this.state.decimalsErrText} - onChange={this._onTokenDecimalsChanged.bind(this)} - /> - </div> - <div className="pt2 mx-auto" style={{ width: 120 }}> - <LifeCycleRaisedButton - labelReady="Add" - labelLoading="Adding..." - labelComplete="Added!" - onClickAsyncFn={this._onAddNewTokenClickAsync.bind(this)} - /> - </div> - {this.state.globalErrMsg !== '' && <Alert type={AlertTypes.Error} message={this.state.globalErrMsg} />} - </div> - ); - } - private async _onAddNewTokenClickAsync(): Promise<void> { - // Trigger validation of name and symbol - this._onTokenNameChanged(undefined, this.state.name); - this._onTokenSymbolChanged(undefined, this.state.symbol); - this._onTokenDecimalsChanged(undefined, this.state.decimals); - - const isAddressIncomplete = this.state.address === ''; - let doesContractExist = false; - if (!isAddressIncomplete) { - doesContractExist = await this.props.blockchain.doesContractExistAtAddressAsync(this.state.address); - } - - let hasBalanceAllowanceErr = false; - if (doesContractExist) { - try { - await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync(this.state.address); - } catch (err) { - hasBalanceAllowanceErr = true; - } - } - - let globalErrMsg = ''; - if ( - this.state.nameErrText !== '' || - this.state.symbolErrText !== '' || - this.state.decimalsErrText !== '' || - isAddressIncomplete - ) { - globalErrMsg = 'Please fix the above issues'; - } else if (!doesContractExist) { - globalErrMsg = 'No contract found at supplied address'; - } else if (hasBalanceAllowanceErr) { - globalErrMsg = 'Unsuccessful call to `balanceOf` and/or `allowance` on supplied contract address'; - } else if (!isAddressIncomplete && !_.isUndefined(this.props.tokenByAddress[this.state.address])) { - globalErrMsg = 'A token already exists with this address'; - } - - if (globalErrMsg !== '') { - this.setState({ - globalErrMsg, - shouldShowAddressIncompleteErr: isAddressIncomplete, - }); - return; - } - - const newToken: Token = { - address: this.state.address, - decimals: _.parseInt(this.state.decimals), - iconUrl: undefined, - name: this.state.name, - symbol: this.state.symbol.toUpperCase(), - trackedTimestamp: moment().unix(), - isRegistered: false, - }; - this.props.onNewTokenSubmitted(newToken); - } - private _onTokenNameChanged(_event: any, name: string): void { - let nameErrText = ''; - const maxLength = 30; - const tokens = _.values(this.props.tokenByAddress); - const tokenWithNameIfExists = _.find(tokens, { name }); - const doesTokenWithNameExists = !_.isUndefined(tokenWithNameIfExists); - if (name === '') { - nameErrText = 'Name is required'; - } else if (!this._isValidName(name)) { - nameErrText = 'Name should only contain letters, digits and spaces'; - } else if (name.length > maxLength) { - nameErrText = `Max length is ${maxLength}`; - } else if (doesTokenWithNameExists) { - nameErrText = 'Token with this name already exists'; - } - - this.setState({ - name, - nameErrText, - }); - } - private _onTokenSymbolChanged(_event: any, symbol: string): void { - let symbolErrText = ''; - const maxLength = 5; - const tokens = _.values(this.props.tokenByAddress); - const doesTokenWithSymbolExists = !_.isUndefined(_.find(tokens, { symbol })); - if (symbol === '') { - symbolErrText = 'Symbol is required'; - } else if (!this._isAlphanumeric(symbol)) { - symbolErrText = 'Can only include alphanumeric characters'; - } else if (symbol.length > maxLength) { - symbolErrText = `Max length is ${maxLength}`; - } else if (doesTokenWithSymbolExists) { - symbolErrText = 'Token with symbol already exists'; - } - - this.setState({ - symbol, - symbolErrText, - }); - } - private _onTokenDecimalsChanged(_event: any, decimals: string): void { - let decimalsErrText = ''; - const maxLength = 2; - if (decimals === '') { - decimalsErrText = 'Decimals is required'; - } else if (!this._isInteger(decimals)) { - decimalsErrText = 'Must be an integer'; - } else if (decimals.length > maxLength) { - decimalsErrText = `Max length is ${maxLength}`; - } - - this.setState({ - decimals, - decimalsErrText, - }); - } - private _onTokenAddressChanged(address?: string): void { - if (!_.isUndefined(address)) { - this.setState({ - address, - }); - } - } - private _isValidName(input: string): boolean { - return /^[a-z0-9 ]+$/i.test(input); - } - private _isInteger(input: string): boolean { - return /^[0-9]+$/i.test(input); - } - private _isAlphanumeric(input: string): boolean { - return /^[a-zA-Z0-9]+$/i.test(input); - } -} |