From d4cb403d511dae6d3c17fa4104bb23bd03e0ef91 Mon Sep 17 00:00:00 2001 From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com> Date: Tue, 18 May 2021 13:23:54 -0400 Subject: [PATCH] Handling custom token decimal fetch failure due to network error (#10956) --- app/_locales/en/messages.json | 9 ++- package.json | 2 +- ui/helpers/utils/token-util.js | 22 +----- ui/pages/add-token/add-token.component.js | 95 ++++++++++++++++++----- ui/pages/add-token/add-token.container.js | 14 +++- ui/pages/add-token/add-token.test.js | 2 +- ui/pages/add-token/index.scss | 13 ++++ ui/store/actions.js | 15 ++-- yarn.lock | 8 +- 9 files changed, 123 insertions(+), 57 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c062bd8d5..5402d0d02 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -486,7 +486,7 @@ "message": "Some of your account data was backed up during a previous installation of MetaMask. This could include your settings, contacts, and tokens. Would you like to restore this data now?" }, "decimal": { - "message": "Decimals of Precision" + "message": "Token Decimal" }, "decimalsMustZerotoTen": { "message": "Decimals must be at least 0, and not over 36." @@ -2221,6 +2221,9 @@ "tokenContractAddress": { "message": "Token Contract Address" }, + "tokenDecimalFetchFailed": { + "message": "Token decimal required." + }, "tokenSymbol": { "message": "Token Symbol" }, @@ -2352,6 +2355,10 @@ "userName": { "message": "Username" }, + "verifyThisTokenDecimalOn": { + "message": "Token decimal can be found on $1", + "description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\"" + }, "verifyThisTokenOn": { "message": "Verify this token on $1", "description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\"" diff --git a/package.json b/package.json index 5509077ca..1ddc3009c 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "@metamask/controllers": "^8.0.0", "@metamask/eth-ledger-bridge-keyring": "^0.5.0", "@metamask/eth-token-tracker": "^3.0.1", - "@metamask/etherscan-link": "^2.0.0", + "@metamask/etherscan-link": "^2.1.0", "@metamask/jazzicon": "^2.0.0", "@metamask/logo": "^2.5.0", "@metamask/obs-store": "^5.0.0", diff --git a/ui/helpers/utils/token-util.js b/ui/helpers/utils/token-util.js index 8811bec5d..4b6d5dd08 100644 --- a/ui/helpers/utils/token-util.js +++ b/ui/helpers/utils/token-util.js @@ -13,7 +13,6 @@ const casedContractMap = Object.keys(contractMap).reduce((acc, base) => { }, {}); const DEFAULT_SYMBOL = ''; -const DEFAULT_DECIMALS = '0'; async function getSymbolFromContract(tokenAddress) { const token = util.getContractAtAddress(tokenAddress); @@ -78,25 +77,6 @@ async function getDecimals(tokenAddress) { return decimals; } -export async function fetchSymbolAndDecimals(tokenAddress) { - let symbol, decimals; - - try { - symbol = await getSymbol(tokenAddress); - decimals = await getDecimals(tokenAddress); - } catch (error) { - log.warn( - `symbol() and decimal() calls for token at address ${tokenAddress} resulted in error:`, - error, - ); - } - - return { - symbol: symbol || DEFAULT_SYMBOL, - decimals: decimals || DEFAULT_DECIMALS, - }; -} - export async function getSymbolAndDecimals(tokenAddress, existingTokens = []) { const existingToken = existingTokens.find( ({ address }) => tokenAddress === address, @@ -123,7 +103,7 @@ export async function getSymbolAndDecimals(tokenAddress, existingTokens = []) { return { symbol: symbol || DEFAULT_SYMBOL, - decimals: decimals || DEFAULT_DECIMALS, + decimals, }; } diff --git a/ui/pages/add-token/add-token.component.js b/ui/pages/add-token/add-token.component.js index f5c7d2077..d5d02312e 100644 --- a/ui/pages/add-token/add-token.component.js +++ b/ui/pages/add-token/add-token.component.js @@ -1,5 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import { getTokenTrackerLink } from '@metamask/etherscan-link'; import { checkExistingAddresses } from '../../helpers/utils/util'; import { tokenInfoGetter } from '../../helpers/utils/token-util'; import { CONFIRM_ADD_TOKEN_ROUTE } from '../../helpers/constants/routes'; @@ -8,6 +9,10 @@ import PageContainer from '../../components/ui/page-container'; import { Tabs, Tab } from '../../components/ui/tabs'; import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; import { addHexPrefix } from '../../../app/scripts/lib/util'; +import ActionableMessage from '../swaps/actionable-message'; +import Typography from '../../components/ui/typography'; +import { TYPOGRAPHY, FONT_WEIGHT } from '../../helpers/constants/design-system'; +import Button from '../../components/ui/button'; import TokenList from './token-list'; import TokenSearch from './token-search'; @@ -30,6 +35,8 @@ class AddToken extends Component { identities: PropTypes.object, showSearchTab: PropTypes.bool.isRequired, mostRecentOverviewPage: PropTypes.string.isRequired, + chainId: PropTypes.string, + rpcPrefs: PropTypes.object, }; state = { @@ -42,8 +49,9 @@ class AddToken extends Component { customAddressError: null, customSymbolError: null, customDecimalsError: null, - autoFilled: false, forceEditSymbol: false, + symbolAutoFilled: false, + decimalAutoFilled: false, }; componentDidMount() { @@ -148,10 +156,11 @@ class AddToken extends Component { } async attemptToAutoFillTokenParams(address) { - const { symbol = '', decimals = 0 } = await this.tokenInfoGetter(address); + const { symbol = '', decimals } = await this.tokenInfoGetter(address); - const autoFilled = Boolean(symbol && decimals); - this.setState({ autoFilled }); + const symbolAutoFilled = Boolean(symbol); + const decimalAutoFilled = Boolean(decimals); + this.setState({ symbolAutoFilled, decimalAutoFilled }); this.handleCustomSymbolChange(symbol || ''); this.handleCustomDecimalsChange(decimals); } @@ -162,7 +171,8 @@ class AddToken extends Component { customAddress, customAddressError: null, tokenSelectorError: null, - autoFilled: false, + symbolAutoFilled: false, + decimalAutoFilled: false, }); const addressIsValid = isValidHexAddress(customAddress, { @@ -213,16 +223,18 @@ class AddToken extends Component { } handleCustomDecimalsChange(value) { - const customDecimals = value.trim(); - const validDecimals = - customDecimals !== null && - customDecimals !== '' && - customDecimals >= MIN_DECIMAL_VALUE && - customDecimals <= MAX_DECIMAL_VALUE; + let customDecimals; let customDecimalsError = null; - if (!validDecimals) { - customDecimalsError = this.context.t('decimalsMustZerotoTen'); + if (value) { + customDecimals = Number(value.trim()); + customDecimalsError = + value < MIN_DECIMAL_VALUE || value > MAX_DECIMAL_VALUE + ? this.context.t('decimalsMustZerotoTen') + : null; + } else { + customDecimals = ''; + customDecimalsError = this.context.t('tokenDecimalFetchFailed'); } this.setState({ customDecimals, customDecimalsError }); @@ -236,10 +248,23 @@ class AddToken extends Component { customAddressError, customSymbolError, customDecimalsError, - autoFilled, forceEditSymbol, + symbolAutoFilled, + decimalAutoFilled, } = this.state; + const { chainId, rpcPrefs } = this.props; + const blockExplorerTokenLink = getTokenTrackerLink( + customAddress, + chainId, + null, + null, + { blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null }, + ); + const blockExplorerLabel = rpcPrefs?.blockExplorerUrl + ? new URL(blockExplorerTokenLink).hostname + : this.context.t('etherscan'); + return (