From a4a5580785bffa58c7242ecd97310507f59beb4e Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 23 Jun 2021 18:28:49 -0500 Subject: [PATCH] Update controllers with conversionRate change with minimal required changes in extension (#11361) * updating controllers with conversionRate change with minimal required changes in extension * swapping showFiat selector in places where possible * adding invalid conversion protection * lint fixes * adjusting list-item styling logic --- package.json | 2 +- .../app/asset-list-item/asset-list-item.js | 2 +- ui/components/app/asset-list/asset-list.js | 18 ++--- .../gas-modal-page-container.container.js | 5 +- .../transaction-breakdown.container.js | 6 +- .../currency-input.container.js | 7 +- ui/components/ui/list-item/index.scss | 9 +++ .../ui/list-item/list-item.component.js | 6 +- .../token-input/token-input.component.test.js | 2 + .../ui/token-input/token-input.container.js | 10 +-- ui/helpers/utils/conversion-util.js | 8 ++- ui/hooks/useCurrencyDisplay.js | 42 ++++++++---- ui/hooks/useUserPreferencedCurrency.js | 8 ++- ui/hooks/useUserPreferencedCurrency.test.js | 11 +++- .../confirm-transaction-base.container.js | 7 +- ui/selectors/custom-gas.js | 11 +--- ui/selectors/selectors.js | 8 ++- yarn.lock | 66 +++++++++---------- 18 files changed, 133 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index 0362be9eb..b3323b9e4 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "@lavamoat/preinstall-always-fail": "^1.0.0", "@material-ui/core": "^4.11.0", "@metamask/contract-metadata": "^1.26.0", - "@metamask/controllers": "^9.0.0", + "@metamask/controllers": "^10.0.0", "@metamask/eth-ledger-bridge-keyring": "^0.6.0", "@metamask/eth-token-tracker": "^3.0.1", "@metamask/etherscan-link": "^2.1.0", diff --git a/ui/components/app/asset-list-item/asset-list-item.js b/ui/components/app/asset-list-item/asset-list-item.js index 3c2325d48..801a8735b 100644 --- a/ui/components/app/asset-list-item/asset-list-item.js +++ b/ui/components/app/asset-list-item/asset-list-item.js @@ -112,7 +112,7 @@ const AssetListItem = ({ } titleIcon={titleIcon} - subtitle={

{secondary}

} + subtitle={secondary ?

{secondary}

: null} onClick={onClick} icon={ { }, ); - const [secondaryCurrencyDisplay] = useCurrencyDisplay( - selectedAccountBalance, - { - numberOfDecimals: secondaryNumberOfDecimals, - currency: secondaryCurrency, - }, - ); + const [ + secondaryCurrencyDisplay, + secondaryCurrencyProperties, + ] = useCurrencyDisplay(selectedAccountBalance, { + numberOfDecimals: secondaryNumberOfDecimals, + currency: secondaryCurrency, + }); const primaryTokenImage = useSelector(getNativeCurrencyImage); @@ -71,7 +71,9 @@ const AssetList = ({ onClickAsset }) => { onClickAsset(nativeCurrency)} data-testid="wallet-balance" - primary={primaryCurrencyProperties.value} + primary={ + primaryCurrencyProperties.value ?? secondaryCurrencyProperties.value + } tokenSymbol={primaryCurrencyProperties.suffix} secondary={showFiat ? secondaryCurrencyDisplay : undefined} tokenImage={primaryTokenImage} diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index 2c7d0aff4..513b4dc86 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -29,7 +29,6 @@ import { getCurrentCurrency, getCurrentEthBalance, getIsMainnet, - getPreferences, getBasicGasEstimateLoadingStatus, getCustomGasLimit, getCustomGasPrice, @@ -39,6 +38,7 @@ import { getAveragePriceEstimateInHexWEI, isCustomPriceExcessive, getIsGasEstimatesFetched, + getShouldShowFiat, } from '../../../../selectors'; import { @@ -112,9 +112,8 @@ const mapStateToProps = (state, ownProps) => { const balance = getCurrentEthBalance(state); - const { showFiatInTestnets } = getPreferences(state); const isMainnet = getIsMainnet(state); - const showFiat = Boolean(isMainnet || showFiatInTestnets); + const showFiat = getShouldShowFiat(state); const newTotalEth = maxModeOn && asset.type === ASSET_TYPES.NATIVE diff --git a/ui/components/app/transaction-breakdown/transaction-breakdown.container.js b/ui/components/app/transaction-breakdown/transaction-breakdown.container.js index 01cdc036e..7650c6faa 100644 --- a/ui/components/app/transaction-breakdown/transaction-breakdown.container.js +++ b/ui/components/app/transaction-breakdown/transaction-breakdown.container.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { getIsMainnet, getPreferences } from '../../../selectors'; +import { getShouldShowFiat } from '../../../selectors'; import { getNativeCurrency } from '../../../ducks/metamask/metamask'; import { getHexGasTotal } from '../../../helpers/utils/confirm-tx.util'; import { sumHexes } from '../../../helpers/utils/transactions.util'; @@ -11,8 +11,6 @@ const mapStateToProps = (state, ownProps) => { txParams: { gas, gasPrice, value } = {}, txReceipt: { gasUsed } = {}, } = transaction; - const { showFiatInTestnets } = getPreferences(state); - const isMainnet = getIsMainnet(state); const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas; @@ -22,7 +20,7 @@ const mapStateToProps = (state, ownProps) => { return { nativeCurrency: getNativeCurrency(state), - showFiat: isMainnet || Boolean(showFiatInTestnets), + showFiat: getShouldShowFiat(state), totalInHex, gas, gasPrice, diff --git a/ui/components/ui/currency-input/currency-input.container.js b/ui/components/ui/currency-input/currency-input.container.js index 23d792838..1d3c09620 100644 --- a/ui/components/ui/currency-input/currency-input.container.js +++ b/ui/components/ui/currency-input/currency-input.container.js @@ -1,20 +1,19 @@ import { connect } from 'react-redux'; import { ETH } from '../../../helpers/constants/common'; -import { getIsMainnet, getPreferences } from '../../../selectors'; +import { getShouldShowFiat } from '../../../selectors'; import CurrencyInput from './currency-input.component'; const mapStateToProps = (state) => { const { metamask: { nativeCurrency, currentCurrency, conversionRate }, } = state; - const { showFiatInTestnets } = getPreferences(state); - const isMainnet = getIsMainnet(state); + const showFiat = getShouldShowFiat(state); return { nativeCurrency, currentCurrency, conversionRate, - hideFiat: !isMainnet && !showFiatInTestnets, + hideFiat: !showFiat, }; }; diff --git a/ui/components/ui/list-item/index.scss b/ui/components/ui/list-item/index.scss index 5470a692c..dc58b90b5 100644 --- a/ui/components/ui/list-item/index.scss +++ b/ui/components/ui/list-item/index.scss @@ -110,3 +110,12 @@ '. actions actions actions actions mid mid mid mid right right right'; } } + +.list-item--single-content-row { + grid-template-areas: 'icon head head head head head head head right right right right'; + align-items: center; + + @media (min-width: 576px) { + grid-template-areas: 'icon head head head head mid mid mid mid right right right'; + } +} diff --git a/ui/components/ui/list-item/list-item.component.js b/ui/components/ui/list-item/list-item.component.js index e6b3da462..61f4c8a58 100644 --- a/ui/components/ui/list-item/list-item.component.js +++ b/ui/components/ui/list-item/list-item.component.js @@ -14,7 +14,11 @@ export default function ListItem({ className, 'data-testid': dataTestId, }) { - const primaryClassName = classnames('list-item', className); + const primaryClassName = classnames( + 'list-item', + className, + subtitle || children ? '' : 'list-item--single-content-row', + ); return (
{ }} tokenExchangeRates={{ '0x1': 2 }} showFiat + currentCurrency="usd" /> , ); @@ -278,6 +279,7 @@ describe('TokenInput Component', () => { }} tokenExchangeRates={{ '0x1': 2 }} showFiat + currentCurrency="usd" /> , ); diff --git a/ui/components/ui/token-input/token-input.container.js b/ui/components/ui/token-input/token-input.container.js index a34c2d247..9f964feec 100644 --- a/ui/components/ui/token-input/token-input.container.js +++ b/ui/components/ui/token-input/token-input.container.js @@ -1,23 +1,17 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { - getIsMainnet, - getTokenExchangeRates, - getPreferences, -} from '../../../selectors'; +import { getTokenExchangeRates, getShouldShowFiat } from '../../../selectors'; import TokenInput from './token-input.component'; const mapStateToProps = (state) => { const { metamask: { currentCurrency }, } = state; - const { showFiatInTestnets } = getPreferences(state); - const isMainnet = getIsMainnet(state); return { currentCurrency, tokenExchangeRates: getTokenExchangeRates(state), - hideConversion: !isMainnet && !showFiatInTestnets, + hideConversion: !getShouldShowFiat(state), }; }; diff --git a/ui/helpers/utils/conversion-util.js b/ui/helpers/utils/conversion-util.js index 3d61f4410..0b550e67b 100644 --- a/ui/helpers/utils/conversion-util.js +++ b/ui/helpers/utils/conversion-util.js @@ -150,8 +150,11 @@ const conversionUtil = ( conversionRate, invertConversionRate, }, -) => - converter({ +) => { + if (fromCurrency !== toCurrency && !conversionRate) { + return 0; + } + return converter({ fromCurrency, toCurrency, fromNumericBase, @@ -163,6 +166,7 @@ const conversionUtil = ( invertConversionRate, value: value || '0', }); +}; const getBigNumber = (value, base) => { if (!isValidBase(base)) { diff --git a/ui/hooks/useCurrencyDisplay.js b/ui/hooks/useCurrencyDisplay.js index 6b770e38e..a1b67213e 100644 --- a/ui/hooks/useCurrencyDisplay.js +++ b/ui/hooks/useCurrencyDisplay.js @@ -10,6 +10,8 @@ import { getNativeCurrency, } from '../ducks/metamask/metamask'; +import { conversionUtil } from '../helpers/utils/conversion-util'; + /** * Defines the shape of the options parameter for useCurrencyDisplay * @typedef {Object} UseCurrencyOptions @@ -45,24 +47,37 @@ export function useCurrencyDisplay( const currentCurrency = useSelector(getCurrentCurrency); const nativeCurrency = useSelector(getNativeCurrency); const conversionRate = useSelector(getConversionRate); - - const toCurrency = currency || currentCurrency; + const isUserPreferredCurrency = currency === currentCurrency; const value = useMemo(() => { if (displayValue) { return displayValue; } - return formatCurrency( - getValueFromWeiHex({ - value: inputValue, - fromCurrency: nativeCurrency, - toCurrency, - conversionRate, + if ( + currency === nativeCurrency || + (!isUserPreferredCurrency && !nativeCurrency) + ) { + return conversionUtil(inputValue, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', numberOfDecimals: numberOfDecimals || 2, toDenomination: denomination, - }), - toCurrency, - ); + }); + } else if (isUserPreferredCurrency && conversionRate) { + return formatCurrency( + getValueFromWeiHex({ + value: inputValue, + fromCurrency: nativeCurrency, + toCurrency: currency, + conversionRate, + numberOfDecimals: numberOfDecimals || 2, + toDenomination: denomination, + }), + currency, + ); + } + return null; }, [ inputValue, nativeCurrency, @@ -70,13 +85,14 @@ export function useCurrencyDisplay( displayValue, numberOfDecimals, denomination, - toCurrency, + currency, + isUserPreferredCurrency, ]); let suffix; if (!opts.hideLabel) { - suffix = opts.suffix || toCurrency.toUpperCase(); + suffix = opts.suffix || currency?.toUpperCase(); } return [ diff --git a/ui/hooks/useUserPreferencedCurrency.js b/ui/hooks/useUserPreferencedCurrency.js index 50b742abf..c41195345 100644 --- a/ui/hooks/useUserPreferencedCurrency.js +++ b/ui/hooks/useUserPreferencedCurrency.js @@ -1,5 +1,9 @@ import { useSelector } from 'react-redux'; -import { getPreferences, getShouldShowFiat } from '../selectors'; +import { + getPreferences, + getShouldShowFiat, + getCurrentCurrency, +} from '../selectors'; import { getNativeCurrency } from '../ducks/metamask/metamask'; import { PRIMARY, SECONDARY, ETH } from '../helpers/constants/common'; @@ -35,6 +39,7 @@ export function useUserPreferencedCurrency(type, opts = {}) { const nativeCurrency = useSelector(getNativeCurrency); const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences); const showFiat = useSelector(getShouldShowFiat); + const currentCurrency = useSelector(getCurrentCurrency); let currency, numberOfDecimals; if ( @@ -50,6 +55,7 @@ export function useUserPreferencedCurrency(type, opts = {}) { (type === PRIMARY && !useNativeCurrencyAsPrimaryCurrency) ) { // Display Fiat + currency = currentCurrency; numberOfDecimals = opts.numberOfDecimals || opts.fiatNumberOfDecimals || 2; } diff --git a/ui/hooks/useUserPreferencedCurrency.test.js b/ui/hooks/useUserPreferencedCurrency.test.js index 5689dc11e..62030b7e3 100644 --- a/ui/hooks/useUserPreferencedCurrency.test.js +++ b/ui/hooks/useUserPreferencedCurrency.test.js @@ -1,7 +1,11 @@ import { renderHook } from '@testing-library/react-hooks'; import * as reactRedux from 'react-redux'; import sinon from 'sinon'; -import { getPreferences, getShouldShowFiat } from '../selectors'; +import { + getCurrentCurrency, + getPreferences, + getShouldShowFiat, +} from '../selectors'; import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'; const tests = [ @@ -24,12 +28,13 @@ const tests = [ useNativeCurrencyAsPrimaryCurrency: false, nativeCurrency: 'ETH', showFiat: true, + currentCurrency: 'usd', }, params: { type: 'PRIMARY', }, result: { - currency: undefined, + currency: 'usd', numberOfDecimals: 2, }, }, @@ -116,6 +121,8 @@ function getFakeUseSelector(state) { return state; } else if (selector === getShouldShowFiat) { return state.showFiat; + } else if (selector === getCurrentCurrency) { + return state.currentCurrency; } return state.nativeCurrency; }; diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index 6af7e49a9..9266be18a 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -32,10 +32,10 @@ import { getKnownMethodData, getMetaMaskAccounts, getUseNonceField, - getPreferences, transactionFeeSelector, getNoGasPriceFetched, getIsEthGasPriceFetched, + getShouldShowFiat, } from '../../selectors'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { transactionMatchesNetwork } from '../../../shared/modules/transaction.utils'; @@ -65,7 +65,6 @@ const mapStateToProps = (state, ownProps) => { match: { params = {} }, } = ownProps; const { id: paramsTransactionId } = params; - const { showFiatInTestnets } = getPreferences(state); const isMainnet = getIsMainnet(state); const { confirmTransaction, metamask } = state; const { @@ -184,8 +183,8 @@ const mapStateToProps = (state, ownProps) => { useNonceField: getUseNonceField(state), customNonceValue, insufficientBalance, - hideSubtitle: !isMainnet && !showFiatInTestnets, - hideFiatConversion: !isMainnet && !showFiatInTestnets, + hideSubtitle: !getShouldShowFiat(state), + hideFiatConversion: !getShouldShowFiat(state), metaMetricsSendCount, type, nextNonce, diff --git a/ui/selectors/custom-gas.js b/ui/selectors/custom-gas.js index c53e37c36..0e275764f 100644 --- a/ui/selectors/custom-gas.js +++ b/ui/selectors/custom-gas.js @@ -12,7 +12,7 @@ import { GAS_ESTIMATE_TYPES } from '../helpers/constants/common'; import { getGasPrice } from '../ducks/send'; import { BASIC_ESTIMATE_STATES, GAS_SOURCE } from '../ducks/gas/gas.duck'; import { GAS_LIMITS } from '../../shared/constants/gas'; -import { getCurrentCurrency, getIsMainnet, getPreferences } from '.'; +import { getCurrentCurrency, getIsMainnet, getShouldShowFiat } from '.'; const NUMBER_OF_DECIMALS_SM_BTNS = 5; @@ -262,9 +262,7 @@ export function getRenderableBasicEstimateData(state, gasLimit) { return []; } - const { showFiatInTestnets } = getPreferences(state); - const isMainnet = getIsMainnet(state); - const showFiat = isMainnet || Boolean(showFiatInTestnets); + const showFiat = getShouldShowFiat(state); const { conversionRate } = state.metamask; const currentCurrency = getCurrentCurrency(state); @@ -287,10 +285,7 @@ export function getRenderableEstimateDataForSmallButtonsFromGWEI(state) { if (getBasicGasEstimateLoadingStatus(state)) { return []; } - - const { showFiatInTestnets } = getPreferences(state); - const isMainnet = getIsMainnet(state); - const showFiat = isMainnet || Boolean(showFiatInTestnets); + const showFiat = getShouldShowFiat(state); const gasLimit = state.send.gas.gasLimit || getCustomGasLimit(state) || GAS_LIMITS.SIMPLE; const { conversionRate } = state.metamask; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index a41005a7e..615d7df51 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -24,7 +24,10 @@ import { TEMPLATED_CONFIRMATION_MESSAGE_TYPES } from '../pages/confirmation/temp import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils'; import { DAY } from '../../shared/constants/time'; -import { getNativeCurrency } from '../ducks/metamask/metamask'; +import { + getNativeCurrency, + getConversionRate, +} from '../ducks/metamask/metamask'; /** * One of the only remaining valid uses of selecting the network subkey of the @@ -379,8 +382,9 @@ export function getPreferences({ metamask }) { export function getShouldShowFiat(state) { const isMainNet = getIsMainnet(state); + const conversionRate = getConversionRate(state); const { showFiatInTestnets } = getPreferences(state); - return Boolean(isMainNet || showFiatInTestnets); + return Boolean((isMainNet || showFiatInTestnets) && conversionRate); } export function getShouldHideZeroBalanceTokens(state) { diff --git a/yarn.lock b/yarn.lock index 4861bf74e..430a702bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2656,6 +2656,39 @@ resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.26.0.tgz#06be4f4dc645da69f6364f75cb2bd47afa642fe6" integrity sha512-58A8csapIPoc854n6AI+3jwJcQfh75BzVrl6SAySgJ9fWTa1XItm9Tx/ORaqYrwaR/9JqH4SnkbXtqyFwuUL6w== +"@metamask/controllers@^10.0.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-10.1.0.tgz#ec0a3751fa658966e9818038784ab6b555c95382" + integrity sha512-Me0jzI5CIOdfXkpm3eBOEIxDGlgbKQaW1au0GQdVgbW93ZxDueTqLUg9xSGSfGSJ3i+Alfqi/tnGqT/nwa/5CQ== + dependencies: + "@metamask/contract-metadata" "^1.25.0" + "@types/uuid" "^8.3.0" + async-mutex "^0.2.6" + babel-runtime "^6.26.0" + eth-ens-namehash "^2.0.8" + eth-json-rpc-infura "^5.1.0" + eth-keyring-controller "^6.2.1" + eth-method-registry "1.1.0" + eth-phishing-detect "^1.1.14" + eth-query "^2.1.2" + eth-rpc-errors "^4.0.0" + eth-sig-util "^3.0.0" + ethereumjs-tx "^1.3.7" + ethereumjs-util "^7.0.10" + ethereumjs-wallet "^1.0.1" + ethjs-util "^0.1.6" + human-standard-collectible-abi "^1.0.2" + human-standard-token-abi "^2.0.0" + immer "^8.0.1" + isomorphic-fetch "^3.0.0" + jsonschema "^1.2.4" + nanoid "^3.1.12" + punycode "^2.1.1" + single-call-balance-checker-abi "^1.0.0" + uuid "^8.3.2" + web3 "^0.20.7" + web3-provider-engine "^16.0.1" + "@metamask/controllers@^5.0.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-5.1.0.tgz#02c1957295bcb6db1655a716d165665d170e7f34" @@ -2684,39 +2717,6 @@ web3 "^0.20.7" web3-provider-engine "^16.0.1" -"@metamask/controllers@^9.0.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-9.1.0.tgz#4434f22eba2522889224b35aa08bc7b67d7248b7" - integrity sha512-jn/F0BNbaPsgEevHaPqk0lGAONKom4re1a4yBC67h7Vu6yu26CRi30SJl4xIh3IW4+ySbPhVLaiXFiXr3fESRQ== - dependencies: - "@metamask/contract-metadata" "^1.25.0" - "@types/uuid" "^8.3.0" - async-mutex "^0.2.6" - babel-runtime "^6.26.0" - eth-ens-namehash "^2.0.8" - eth-json-rpc-infura "^5.1.0" - eth-keyring-controller "^6.2.1" - eth-method-registry "1.1.0" - eth-phishing-detect "^1.1.14" - eth-query "^2.1.2" - eth-rpc-errors "^4.0.0" - eth-sig-util "^3.0.0" - ethereumjs-tx "^1.3.7" - ethereumjs-util "^6.1.0" - ethereumjs-wallet "^1.0.1" - ethjs-util "^0.1.6" - human-standard-collectible-abi "^1.0.2" - human-standard-token-abi "^2.0.0" - immer "^8.0.1" - isomorphic-fetch "^3.0.0" - jsonschema "^1.2.4" - nanoid "^3.1.12" - punycode "^2.1.1" - single-call-balance-checker-abi "^1.0.0" - uuid "^8.3.2" - web3 "^0.20.7" - web3-provider-engine "^16.0.1" - "@metamask/eslint-config-jest@^6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@metamask/eslint-config-jest/-/eslint-config-jest-6.0.0.tgz#9e10cfbca31236afd7be2058be70365084e540d6"