import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ethUtil from 'ethereumjs-util'; import { checkExistingAddresses } from '../../helpers/utils/util'; import { tokenInfoGetter } from '../../helpers/utils/token-util'; import { CONFIRM_ADD_TOKEN_ROUTE } from '../../helpers/constants/routes'; import TextField from '../../components/ui/text-field'; import PageContainer from '../../components/ui/page-container'; import { Tabs, Tab } from '../../components/ui/tabs'; import { addHexPrefix } from '../../../../app/scripts/lib/util'; import TokenList from './token-list'; import TokenSearch from './token-search'; const emptyAddr = '0x0000000000000000000000000000000000000000'; const MIN_DECIMAL_VALUE = 0; const MAX_DECIMAL_VALUE = 36; class AddToken extends Component { static contextTypes = { t: PropTypes.func, }; static propTypes = { history: PropTypes.object, setPendingTokens: PropTypes.func, pendingTokens: PropTypes.object, clearPendingTokens: PropTypes.func, tokens: PropTypes.array, identities: PropTypes.object, showSearchTab: PropTypes.bool.isRequired, mostRecentOverviewPage: PropTypes.string.isRequired, }; state = { customAddress: '', customSymbol: '', customDecimals: 0, searchResults: [], selectedTokens: {}, tokenSelectorError: null, customAddressError: null, customSymbolError: null, customDecimalsError: null, autoFilled: false, forceEditSymbol: false, }; componentDidMount() { this.tokenInfoGetter = tokenInfoGetter(); const { pendingTokens = {} } = this.props; const pendingTokenKeys = Object.keys(pendingTokens); if (pendingTokenKeys.length > 0) { let selectedTokens = {}; let customToken = {}; pendingTokenKeys.forEach((tokenAddress) => { const token = pendingTokens[tokenAddress]; const { isCustom } = token; if (isCustom) { customToken = { ...token }; } else { selectedTokens = { ...selectedTokens, [tokenAddress]: { ...token } }; } }); const { address: customAddress = '', symbol: customSymbol = '', decimals: customDecimals = 0, } = customToken; this.setState({ selectedTokens, customAddress, customSymbol, customDecimals, }); } } handleToggleToken(token) { const { address } = token; const { selectedTokens = {} } = this.state; const selectedTokensCopy = { ...selectedTokens }; if (address in selectedTokensCopy) { delete selectedTokensCopy[address]; } else { selectedTokensCopy[address] = token; } this.setState({ selectedTokens: selectedTokensCopy, tokenSelectorError: null, }); } hasError() { const { tokenSelectorError, customAddressError, customSymbolError, customDecimalsError, } = this.state; return ( tokenSelectorError || customAddressError || customSymbolError || customDecimalsError ); } hasSelected() { const { customAddress = '', selectedTokens = {} } = this.state; return customAddress || Object.keys(selectedTokens).length > 0; } handleNext() { if (this.hasError()) { return; } if (!this.hasSelected()) { this.setState({ tokenSelectorError: this.context.t('mustSelectOne') }); return; } const { setPendingTokens, history } = this.props; const { customAddress: address, customSymbol: symbol, customDecimals: decimals, selectedTokens, } = this.state; const customToken = { address, symbol, decimals, }; setPendingTokens({ customToken, selectedTokens }); history.push(CONFIRM_ADD_TOKEN_ROUTE); } async attemptToAutoFillTokenParams(address) { const { symbol = '', decimals = 0 } = await this.tokenInfoGetter(address); const autoFilled = Boolean(symbol && decimals); this.setState({ autoFilled }); this.handleCustomSymbolChange(symbol || ''); this.handleCustomDecimalsChange(decimals); } handleCustomAddressChange(value) { const customAddress = value.trim(); this.setState({ customAddress, customAddressError: null, tokenSelectorError: null, autoFilled: false, }); const isValidAddress = ethUtil.isValidAddress(customAddress); const standardAddress = addHexPrefix(customAddress).toLowerCase(); switch (true) { case !isValidAddress: this.setState({ customAddressError: this.context.t('invalidAddress'), customSymbol: '', customDecimals: 0, customSymbolError: null, customDecimalsError: null, }); break; case Boolean(this.props.identities[standardAddress]): this.setState({ customAddressError: this.context.t('personalAddressDetected'), }); break; case checkExistingAddresses(customAddress, this.props.tokens): this.setState({ customAddressError: this.context.t('tokenAlreadyAdded'), }); break; default: if (customAddress !== emptyAddr) { this.attemptToAutoFillTokenParams(customAddress); } } } handleCustomSymbolChange(value) { const customSymbol = value.trim(); const symbolLength = customSymbol.length; let customSymbolError = null; if (symbolLength <= 0 || symbolLength >= 12) { customSymbolError = this.context.t('symbolBetweenZeroTwelve'); } this.setState({ customSymbol, customSymbolError }); } handleCustomDecimalsChange(value) { const customDecimals = value.trim(); const validDecimals = customDecimals !== null && customDecimals !== '' && customDecimals >= MIN_DECIMAL_VALUE && customDecimals <= MAX_DECIMAL_VALUE; let customDecimalsError = null; if (!validDecimals) { customDecimalsError = this.context.t('decimalsMustZerotoTen'); } this.setState({ customDecimals, customDecimalsError }); } renderCustomTokenForm() { const { customAddress, customSymbol, customDecimals, customAddressError, customSymbolError, customDecimalsError, autoFilled, forceEditSymbol, } = this.state; return (