diff --git a/.eslintrc.js b/.eslintrc.js index 9379efb55..13fa5251c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -269,6 +269,7 @@ module.exports = { 'test/jest/*.js', 'ui/**/*.test.js', 'ui/__mocks__/*.js', + 'shared/lib/error-utils.test.js', ], extends: ['@metamask/eslint-config-jest'], parserOptions: { diff --git a/.mocharc.js b/.mocharc.js index e3904c2b1..2db1510e4 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -7,6 +7,7 @@ module.exports = { './app/scripts/platforms/*.test.js', './app/scripts/controllers/network/**/*.test.js', './app/scripts/controllers/permissions/**/*.test.js', + './app/scripts/constants/error-utils.test.js', ], recursive: true, require: ['test/env.js', 'test/setup.js'], diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index e49eff3c5..e1baff4e1 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -4,8 +4,6 @@ import BigNumber from 'bignumber.js'; import { ObservableStore } from '@metamask/obs-store'; import { mapValues, cloneDeep } from 'lodash'; import abi from 'human-standard-token-abi'; -import { calcTokenAmount } from '../../../ui/helpers/utils/token-util'; -import { calcGasTotal } from '../../../ui/pages/send/send.utils'; import { conversionUtil, decGWEIToHexWEI, @@ -30,10 +28,15 @@ import { isSwapsDefaultTokenAddress } from '../../../shared/modules/swaps.utils' import { fetchTradesInfo as defaultFetchTradesInfo, getBaseApi, -} from '../../../ui/pages/swaps/swaps.util'; -import fetchWithCache from '../../../ui/helpers/utils/fetch-with-cache'; +} from '../../../shared/lib/swaps-utils'; +import fetchWithCache from '../../../shared/lib/fetch-with-cache'; import { MINUTE, SECOND } from '../../../shared/constants/time'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; +import { + calcGasTotal, + calcTokenAmount, +} from '../../../shared/lib/transactions-controller-utils'; + import { NETWORK_EVENTS } from './network'; // The MAX_GAS_LIMIT is a number that is higher than the maximum gas costs we have observed on any aggregator diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index f314ae33f..29e71e22f 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -17,13 +17,6 @@ import { addHexPrefix, getChainType, } from '../../lib/util'; -import { calcGasTotal } from '../../../../ui/pages/send/send.utils'; -import { getSwapsTokensReceivedFromTxMeta } from '../../../../ui/pages/swaps/swaps.util'; -import { - hexWEIToDecGWEI, - decimalToHex, - hexWEIToDecETH, -} from '../../../../ui/helpers/utils/conversions.util'; import { TRANSACTION_STATUSES, TRANSACTION_TYPES, @@ -32,7 +25,6 @@ import { TRANSACTION_ENVELOPE_TYPES, TRANSACTION_EVENTS, } from '../../../../shared/constants/transaction'; -import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../ui/helpers/constants/transactions'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; import { GAS_LIMITS, @@ -56,6 +48,14 @@ import { isEIP1559Transaction, } from '../../../../shared/modules/transaction.utils'; import { ORIGIN_METAMASK } from '../../../../shared/constants/app'; +import { + calcGasTotal, + decimalToHex, + getSwapsTokensReceivedFromTxMeta, + hexWEIToDecETH, + hexWEIToDecGWEI, + TRANSACTION_ENVELOPE_TYPE_NAMES, +} from '../../../../shared/lib/transactions-controller-utils'; import TransactionStateManager from './tx-state-manager'; import TxGasUtil from './tx-gas-utils'; import PendingTransactionTracker from './pending-tx-tracker'; diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index a1c3b6db7..4bd5da250 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -25,9 +25,9 @@ import { GAS_ESTIMATE_TYPES, GAS_RECOMMENDATIONS, } from '../../../../shared/constants/gas'; -import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../ui/helpers/constants/transactions'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; import { ORIGIN_METAMASK } from '../../../../shared/constants/app'; +import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../shared/lib/transactions-controller-utils'; import TransactionController from '.'; const noop = () => true; diff --git a/app/scripts/lib/metaRPCClientFactory.js b/app/scripts/lib/metaRPCClientFactory.js index c69651c01..c09f7e38d 100644 --- a/app/scripts/lib/metaRPCClientFactory.js +++ b/app/scripts/lib/metaRPCClientFactory.js @@ -1,7 +1,7 @@ import { EthereumRpcError } from 'eth-rpc-errors'; import SafeEventEmitter from 'safe-event-emitter'; import createRandomId from '../../../shared/modules/random-id'; -import { TEN_SECONDS_IN_MILLISECONDS } from '../../../ui/helpers/constants/critical-error'; +import { TEN_SECONDS_IN_MILLISECONDS } from '../../../shared/lib/transactions-controller-utils'; class DisconnectError extends Error {} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index fac7e14ad..6022e6927 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -90,14 +90,14 @@ import { } from '../../shared/constants/app'; import { EVENT, EVENT_NAMES } from '../../shared/constants/metametrics'; -import { hexToDecimal } from '../../ui/helpers/utils/conversions.util'; -import { - getTokenIdParam, - getTokenValueParam, -} from '../../ui/helpers/utils/token-util'; +import { getTokenIdParam } from '../../ui/helpers/utils/token-util'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens'; +import { + getTokenValueParam, + hexToDecimal, +} from '../../shared/lib/metamask-controller-utils'; import { onMessageReceived, checkForMultipleVersionsRunning, diff --git a/app/scripts/ui.js b/app/scripts/ui.js index de074300e..deb12f6c1 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -17,8 +17,8 @@ import { ENVIRONMENT_TYPE_POPUP, } from '../../shared/constants/app'; import { isManifestV3 } from '../../shared/modules/mv3.utils'; -import { SUPPORT_LINK } from '../../ui/helpers/constants/common'; -import { getErrorHtml } from '../../ui/helpers/utils/error-utils'; +import { SUPPORT_LINK } from '../../shared/lib/ui-utils'; +import { getErrorHtml } from '../../shared/lib/error-utils'; import ExtensionPlatform from './platforms/extension'; import { setupMultiplex } from './lib/stream-utils'; import { getEnvironmentType } from './lib/util'; diff --git a/development/verify-locale-strings.js b/development/verify-locale-strings.js index 26b01ad89..d9f27810a 100755 --- a/development/verify-locale-strings.js +++ b/development/verify-locale-strings.js @@ -177,9 +177,12 @@ async function verifyEnglishLocale() { 'ui/pages/confirmation/templates/*.js', ]; const testGlob = '**/*.test.js'; - const javascriptFiles = await glob(['ui/**/*.js', 'shared/**/*.js'], { - ignore: [...globsToStrictSearch, testGlob], - }); + const javascriptFiles = await glob( + ['ui/**/*.js', 'shared/**/*.js', 'app/scripts/constants/**/*.js'], + { + ignore: [...globsToStrictSearch, testGlob], + }, + ); const javascriptFilesToStrictSearch = await glob(globsToStrictSearch, { ignore: [testGlob], }); diff --git a/jest.config.js b/jest.config.js index 06d275fb8..5a2c7f600 100644 --- a/jest.config.js +++ b/jest.config.js @@ -42,6 +42,7 @@ module.exports = { 'app/scripts/controllers/network/**/*.test.js', '/app/scripts/controllers/permissions/**/*.test.js', '/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js', + '/app/scripts/constants/error-utils.test.js', ], testTimeout: 2500, // We have to specify the environment we are running in, which is jsdom. The diff --git a/ui/helpers/utils/error-utils.js b/shared/lib/error-utils.js similarity index 70% rename from ui/helpers/utils/error-utils.js rename to shared/lib/error-utils.js index a33a056fb..c0baad2c1 100644 --- a/ui/helpers/utils/error-utils.js +++ b/shared/lib/error-utils.js @@ -1,7 +1,27 @@ -import getFirstPreferredLangCode from '../../../app/scripts/lib/get-first-preferred-lang-code'; -import { setupLocale } from '../..'; +import { memoize } from 'lodash'; +import getFirstPreferredLangCode from '../../app/scripts/lib/get-first-preferred-lang-code'; +import { + fetchLocale, + loadRelativeTimeFormatLocaleData, +} from '../../ui/helpers/utils/i18n-helper'; import switchDirection from './switch-direction'; +const _setupLocale = async (currentLocale) => { + const currentLocaleMessages = currentLocale + ? await fetchLocale(currentLocale) + : {}; + const enLocaleMessages = await fetchLocale('en'); + + await loadRelativeTimeFormatLocaleData('en'); + if (currentLocale) { + await loadRelativeTimeFormatLocaleData(currentLocale); + } + + return { currentLocaleMessages, enLocaleMessages }; +}; + +export const setupLocale = memoize(_setupLocale); + const getLocaleContext = (currentLocaleMessages, enLocaleMessages) => { return (key) => { let message = currentLocaleMessages[key]?.message; diff --git a/ui/helpers/utils/error-utils.test.js b/shared/lib/error-utils.test.js similarity index 84% rename from ui/helpers/utils/error-utils.test.js rename to shared/lib/error-utils.test.js index 84aa05508..e1a121d7f 100644 --- a/ui/helpers/utils/error-utils.test.js +++ b/shared/lib/error-utils.test.js @@ -1,14 +1,14 @@ -import { SUPPORT_LINK } from '../constants/common'; +import { fetchLocale } from '../../ui/helpers/utils/i18n-helper'; +import { SUPPORT_LINK } from './ui-utils'; import { getErrorHtml } from './error-utils'; -import { fetchLocale } from './i18n-helper'; -jest.mock('./i18n-helper', () => ({ +jest.mock('../../ui/helpers/utils/i18n-helper', () => ({ fetchLocale: jest.fn(), loadRelativeTimeFormatLocaleData: jest.fn(), })); -describe('Error utils Tests', () => { - it('should get error html', async () => { +describe('Error utils Tests', function () { + it('should get error html', async function () { const mockStore = { localeMessages: { current: { diff --git a/ui/helpers/utils/fetch-with-cache.js b/shared/lib/fetch-with-cache.js similarity index 92% rename from ui/helpers/utils/fetch-with-cache.js rename to shared/lib/fetch-with-cache.js index 377c3d51f..a92f4fb2e 100644 --- a/ui/helpers/utils/fetch-with-cache.js +++ b/shared/lib/fetch-with-cache.js @@ -1,5 +1,5 @@ -import { MINUTE, SECOND } from '../../../shared/constants/time'; -import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'; +import { MINUTE, SECOND } from '../constants/time'; +import getFetchWithTimeout from '../modules/fetch-with-timeout'; import { getStorageItem, setStorageItem } from './storage-helpers'; const fetchWithCache = async ( diff --git a/ui/helpers/utils/fetch-with-cache.test.js b/shared/lib/fetch-with-cache.test.js similarity index 99% rename from ui/helpers/utils/fetch-with-cache.test.js rename to shared/lib/fetch-with-cache.test.js index 76e7ebd01..0b6f3f933 100644 --- a/ui/helpers/utils/fetch-with-cache.test.js +++ b/shared/lib/fetch-with-cache.test.js @@ -3,7 +3,7 @@ import sinon from 'sinon'; import { getStorageItem, setStorageItem } from './storage-helpers'; -jest.mock('./storage-helpers.js', () => ({ +jest.mock('./storage-helpers', () => ({ getStorageItem: jest.fn(), setStorageItem: jest.fn(), })); diff --git a/shared/lib/metamask-controller-utils.js b/shared/lib/metamask-controller-utils.js new file mode 100644 index 000000000..77b171fe5 --- /dev/null +++ b/shared/lib/metamask-controller-utils.js @@ -0,0 +1,12 @@ +import { conversionUtil } from '../modules/conversion.utils'; + +export function hexToDecimal(hexValue) { + return conversionUtil(hexValue, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + }); +} + +export function getTokenValueParam(tokenData = {}) { + return tokenData?.args?._value?.toString(); +} diff --git a/ui/helpers/utils/storage-helpers.js b/shared/lib/storage-helpers.js similarity index 100% rename from ui/helpers/utils/storage-helpers.js rename to shared/lib/storage-helpers.js diff --git a/shared/lib/swaps-utils.js b/shared/lib/swaps-utils.js new file mode 100644 index 000000000..6981ef255 --- /dev/null +++ b/shared/lib/swaps-utils.js @@ -0,0 +1,320 @@ +import BigNumber from 'bignumber.js'; +import log from 'loglevel'; +import { CHAIN_IDS } from '../constants/network'; +import { + GAS_API_BASE_URL, + GAS_DEV_API_BASE_URL, + SWAPS_API_V2_BASE_URL, + SWAPS_CHAINID_DEFAULT_TOKEN_MAP, + SWAPS_CLIENT_ID, + SWAPS_DEV_API_V2_BASE_URL, + SWAPS_WRAPPED_TOKENS_ADDRESSES, +} from '../constants/swaps'; +import { SECOND } from '../constants/time'; +import { isValidHexAddress } from '../modules/hexstring-utils'; +import { addHexPrefix } from '../../app/scripts/lib/util'; +import fetchWithCache from './fetch-with-cache'; +import { decimalToHex } from './transactions-controller-utils'; + +const TEST_CHAIN_IDS = [CHAIN_IDS.GOERLI, CHAIN_IDS.LOCALHOST]; + +const clientIdHeader = { 'X-Client-Id': SWAPS_CLIENT_ID }; + +export const validHex = (string) => Boolean(string?.match(/^0x[a-f0-9]+$/u)); +export const truthyString = (string) => Boolean(string?.length); +export const truthyDigitString = (string) => + truthyString(string) && Boolean(string.match(/^\d+$/u)); + +export function validateData(validators, object, urlUsed, logError = true) { + return validators.every(({ property, type, validator }) => { + const types = type.split('|'); + + const valid = + types.some((_type) => typeof object[property] === _type) && + (!validator || validator(object[property])); + if (!valid && logError) { + log.error( + `response to GET ${urlUsed} invalid for property ${property}; value was:`, + object[property], + '| type was: ', + typeof object[property], + ); + } + return valid; + }); +} + +export const QUOTE_VALIDATORS = [ + { + property: 'trade', + type: 'object', + validator: (trade) => + trade && + validHex(trade.data) && + isValidHexAddress(trade.to, { allowNonPrefixed: false }) && + isValidHexAddress(trade.from, { allowNonPrefixed: false }) && + truthyString(trade.value), + }, + { + property: 'approvalNeeded', + type: 'object', + validator: (approvalTx) => + approvalTx === null || + (approvalTx && + validHex(approvalTx.data) && + isValidHexAddress(approvalTx.to, { allowNonPrefixed: false }) && + isValidHexAddress(approvalTx.from, { allowNonPrefixed: false })), + }, + { + property: 'sourceAmount', + type: 'string', + validator: truthyDigitString, + }, + { + property: 'destinationAmount', + type: 'string', + validator: truthyDigitString, + }, + { + property: 'sourceToken', + type: 'string', + validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }), + }, + { + property: 'destinationToken', + type: 'string', + validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }), + }, + { + property: 'aggregator', + type: 'string', + validator: truthyString, + }, + { + property: 'aggType', + type: 'string', + validator: truthyString, + }, + { + property: 'error', + type: 'object', + validator: (error) => error === null || typeof error === 'object', + }, + { + property: 'averageGas', + type: 'number', + }, + { + property: 'maxGas', + type: 'number', + }, + { + property: 'gasEstimate', + type: 'number|undefined', + validator: (gasEstimate) => gasEstimate === undefined || gasEstimate > 0, + }, + { + property: 'fee', + type: 'number', + }, +]; + +/** + * @param {string} type - Type of an API call, e.g. "tokens" + * @param {string} chainId + * @returns string + */ +const getBaseUrlForNewSwapsApi = (type, chainId) => { + const useDevApis = process.env.SWAPS_USE_DEV_APIS; + const v2ApiBaseUrl = useDevApis + ? SWAPS_DEV_API_V2_BASE_URL + : SWAPS_API_V2_BASE_URL; + const gasApiBaseUrl = useDevApis ? GAS_DEV_API_BASE_URL : GAS_API_BASE_URL; + const noNetworkSpecificTypes = ['refreshTime']; // These types don't need network info in the URL. + if (noNetworkSpecificTypes.includes(type)) { + return v2ApiBaseUrl; + } + const chainIdDecimal = chainId && parseInt(chainId, 16); + const gasApiTypes = ['gasPrices']; + if (gasApiTypes.includes(type)) { + return `${gasApiBaseUrl}/networks/${chainIdDecimal}`; // Gas calculations are in its own repo. + } + return `${v2ApiBaseUrl}/networks/${chainIdDecimal}`; +}; + +export const getBaseApi = function (type, chainId = CHAIN_IDS.MAINNET) { + // eslint-disable-next-line no-param-reassign + chainId = TEST_CHAIN_IDS.includes(chainId) ? CHAIN_IDS.MAINNET : chainId; + const baseUrl = getBaseUrlForNewSwapsApi(type, chainId); + const chainIdDecimal = chainId && parseInt(chainId, 16); + if (!baseUrl) { + throw new Error(`Swaps API calls are disabled for chainId: ${chainId}`); + } + switch (type) { + case 'trade': + return `${baseUrl}/trades?`; + case 'tokens': + return `${baseUrl}/tokens`; + case 'token': + return `${baseUrl}/token`; + case 'topAssets': + return `${baseUrl}/topAssets`; + case 'aggregatorMetadata': + return `${baseUrl}/aggregatorMetadata`; + case 'gasPrices': + return `${baseUrl}/gasPrices`; + case 'network': + // Only use v2 for this endpoint. + return `${SWAPS_API_V2_BASE_URL}/networks/${chainIdDecimal}`; + default: + throw new Error('getBaseApi requires an api call type'); + } +}; + +export function calcTokenValue(value, decimals) { + const multiplier = Math.pow(10, Number(decimals || 0)); + return new BigNumber(String(value)).times(multiplier); +} + +export const shouldEnableDirectWrapping = ( + chainId, + sourceToken, + destinationToken, +) => { + if (!sourceToken || !destinationToken) { + return false; + } + const wrappedToken = SWAPS_WRAPPED_TOKENS_ADDRESSES[chainId]; + const nativeToken = SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.address; + const sourceTokenLowerCase = sourceToken.toLowerCase(); + const destinationTokenLowerCase = destinationToken.toLowerCase(); + return ( + (sourceTokenLowerCase === wrappedToken && + destinationTokenLowerCase === nativeToken) || + (sourceTokenLowerCase === nativeToken && + destinationTokenLowerCase === wrappedToken) + ); +}; + +/** + * Given and object where all values are strings, returns the same object with all values + * now prefixed with '0x' + * + * @param obj + */ +export function addHexPrefixToObjectValues(obj) { + return Object.keys(obj).reduce((newObj, key) => { + return { ...newObj, [key]: addHexPrefix(obj[key]) }; + }, {}); +} + +/** + * Given the standard set of information about a transaction, returns a transaction properly formatted for + * publishing via JSON RPC and web3 + * + * @param {object} options + * @param {boolean} [options.sendToken] - Indicates whether or not the transaciton is a token transaction + * @param {string} options.data - A hex string containing the data to include in the transaction + * @param {string} options.to - A hex address of the tx recipient address + * @param options.amount + * @param {string} options.from - A hex address of the tx sender address + * @param {string} options.gas - A hex representation of the gas value for the transaction + * @param {string} options.gasPrice - A hex representation of the gas price for the transaction + * @returns {object} An object ready for submission to the blockchain, with all values appropriately hex prefixed + */ +export function constructTxParams({ + sendToken, + data, + to, + amount, + from, + gas, + gasPrice, +}) { + const txParams = { + data, + from, + value: '0', + gas, + gasPrice, + }; + + if (!sendToken) { + txParams.value = amount; + txParams.to = to; + } + return addHexPrefixToObjectValues(txParams); +} + +export async function fetchTradesInfo( + { + slippage, + sourceToken, + sourceDecimals, + destinationToken, + value, + fromAddress, + exchangeList, + }, + { chainId }, +) { + const urlParams = { + destinationToken, + sourceToken, + sourceAmount: calcTokenValue(value, sourceDecimals).toString(10), + slippage, + timeout: SECOND * 10, + walletAddress: fromAddress, + }; + + if (exchangeList) { + urlParams.exchangeList = exchangeList; + } + if (shouldEnableDirectWrapping(chainId, sourceToken, destinationToken)) { + urlParams.enableDirectWrapping = true; + } + + const queryString = new URLSearchParams(urlParams).toString(); + const tradeURL = `${getBaseApi('trade', chainId)}${queryString}`; + const tradesResponse = await fetchWithCache( + tradeURL, + { method: 'GET', headers: clientIdHeader }, + { cacheRefreshTime: 0, timeout: SECOND * 15 }, + ); + const newQuotes = tradesResponse.reduce((aggIdTradeMap, quote) => { + if ( + quote.trade && + !quote.error && + validateData(QUOTE_VALIDATORS, quote, tradeURL) + ) { + const constructedTrade = constructTxParams({ + to: quote.trade.to, + from: quote.trade.from, + data: quote.trade.data, + amount: decimalToHex(quote.trade.value), + gas: decimalToHex(quote.maxGas), + }); + + let { approvalNeeded } = quote; + + if (approvalNeeded) { + approvalNeeded = constructTxParams({ + ...approvalNeeded, + }); + } + + return { + ...aggIdTradeMap, + [quote.aggregator]: { + ...quote, + slippage, + trade: constructedTrade, + approvalNeeded, + }, + }; + } + return aggIdTradeMap; + }, {}); + + return newQuotes; +} diff --git a/ui/helpers/utils/switch-direction.js b/shared/lib/switch-direction.js similarity index 100% rename from ui/helpers/utils/switch-direction.js rename to shared/lib/switch-direction.js diff --git a/shared/lib/token-util.js b/shared/lib/token-util.js new file mode 100644 index 000000000..d8c2927e8 --- /dev/null +++ b/shared/lib/token-util.js @@ -0,0 +1,20 @@ +/** + * Gets the '_value' parameter of the given token transaction data + * (i.e function call) per the Human Standard Token ABI, if present. + * + * @param {object} tokenData - ethers Interface token data. + * @returns {string | undefined} A decimal string value. + */ +/** + * Gets either the '_tokenId' parameter or the 'id' param of the passed token transaction data., + * These are the parsed tokenId values returned by `parseStandardTokenTransactionData` as defined + * in the ERC721 and ERC1155 ABIs from metamask-eth-abis (https://github.com/MetaMask/metamask-eth-abis/tree/main/src/abis) + * + * @param {object} tokenData - ethers Interface token data. + * @returns {string | undefined} A decimal string value. + */ +export function getTokenIdParam(tokenData = {}) { + return ( + tokenData?.args?._tokenId?.toString() ?? tokenData?.args?.id?.toString() + ); +} diff --git a/shared/lib/transactions-controller-utils.js b/shared/lib/transactions-controller-utils.js new file mode 100644 index 000000000..bc6cec5ad --- /dev/null +++ b/shared/lib/transactions-controller-utils.js @@ -0,0 +1,172 @@ +import BigNumber from 'bignumber.js'; +import { TRANSACTION_ENVELOPE_TYPES } from '../constants/transaction'; +import { + conversionUtil, + multiplyCurrencies, + subtractCurrencies, +} from '../modules/conversion.utils'; +import { isSwapsDefaultTokenSymbol } from '../modules/swaps.utils'; + +const TOKEN_TRANSFER_LOG_TOPIC_HASH = + '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; + +export const TRANSACTION_NO_CONTRACT_ERROR_KEY = 'transactionErrorNoContract'; + +export const TEN_SECONDS_IN_MILLISECONDS = 10_000; + +export function calcGasTotal(gasLimit = '0', gasPrice = '0') { + return multiplyCurrencies(gasLimit, gasPrice, { + toNumericBase: 'hex', + multiplicandBase: 16, + multiplierBase: 16, + }); +} + +/** + * Given a number and specified precision, returns that number in base 10 with a maximum of precision + * significant digits, but without any trailing zeros after the decimal point To be used when wishing + * to display only as much digits to the user as necessary + * + * @param {string | number | BigNumber} n - The number to format + * @param {number} precision - The maximum number of significant digits in the return value + * @returns {string} The number in decimal form, with <= precision significant digits and no decimal trailing zeros + */ +export function toPrecisionWithoutTrailingZeros(n, precision) { + return new BigNumber(n) + .toPrecision(precision) + .replace(/(\.[0-9]*[1-9])0*|(\.0*)/u, '$1'); +} + +export function calcTokenAmount(value, decimals) { + const multiplier = Math.pow(10, Number(decimals || 0)); + return new BigNumber(String(value)).div(multiplier); +} + +export function getSwapsTokensReceivedFromTxMeta( + tokenSymbol, + txMeta, + tokenAddress, + accountAddress, + tokenDecimals, + approvalTxMeta, + chainId, +) { + const txReceipt = txMeta?.txReceipt; + const networkAndAccountSupports1559 = + txMeta?.txReceipt?.type === TRANSACTION_ENVELOPE_TYPES.FEE_MARKET; + if (isSwapsDefaultTokenSymbol(tokenSymbol, chainId)) { + if ( + !txReceipt || + !txMeta || + !txMeta.postTxBalance || + !txMeta.preTxBalance + ) { + return null; + } + + if (txMeta.swapMetaData && txMeta.preTxBalance === txMeta.postTxBalance) { + // If preTxBalance and postTxBalance are equal, postTxBalance hasn't been updated on time + // because of the RPC provider delay, so we return an estimated receiving amount instead. + return txMeta.swapMetaData.token_to_amount; + } + + let approvalTxGasCost = '0x0'; + if (approvalTxMeta && approvalTxMeta.txReceipt) { + approvalTxGasCost = calcGasTotal( + approvalTxMeta.txReceipt.gasUsed, + networkAndAccountSupports1559 + ? approvalTxMeta.txReceipt.effectiveGasPrice // Base fee + priority fee. + : approvalTxMeta.txParams.gasPrice, + ); + } + + const gasCost = calcGasTotal( + txReceipt.gasUsed, + networkAndAccountSupports1559 + ? txReceipt.effectiveGasPrice + : txMeta.txParams.gasPrice, + ); + const totalGasCost = new BigNumber(gasCost, 16) + .plus(approvalTxGasCost, 16) + .toString(16); + + const preTxBalanceLessGasCost = subtractCurrencies( + txMeta.preTxBalance, + totalGasCost, + { + aBase: 16, + bBase: 16, + toNumericBase: 'hex', + }, + ); + + const ethReceived = subtractCurrencies( + txMeta.postTxBalance, + preTxBalanceLessGasCost, + { + aBase: 16, + bBase: 16, + fromDenomination: 'WEI', + toDenomination: 'ETH', + toNumericBase: 'dec', + numberOfDecimals: 6, + }, + ); + return ethReceived; + } + const txReceiptLogs = txReceipt?.logs; + if (txReceiptLogs && txReceipt?.status !== '0x0') { + const tokenTransferLog = txReceiptLogs.find((txReceiptLog) => { + const isTokenTransfer = + txReceiptLog.topics && + txReceiptLog.topics[0] === TOKEN_TRANSFER_LOG_TOPIC_HASH; + const isTransferFromGivenToken = txReceiptLog.address === tokenAddress; + const isTransferFromGivenAddress = + txReceiptLog.topics && + txReceiptLog.topics[2] && + txReceiptLog.topics[2].match(accountAddress.slice(2)); + return ( + isTokenTransfer && + isTransferFromGivenToken && + isTransferFromGivenAddress + ); + }); + return tokenTransferLog + ? toPrecisionWithoutTrailingZeros( + calcTokenAmount(tokenTransferLog.data, tokenDecimals).toString(10), + 6, + ) + : ''; + } + return null; +} + +export const TRANSACTION_ENVELOPE_TYPE_NAMES = { + FEE_MARKET: 'fee-market', + LEGACY: 'legacy', +}; + +export function hexWEIToDecGWEI(decGWEI) { + return conversionUtil(decGWEI, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', + toDenomination: 'GWEI', + }); +} + +export function decimalToHex(decimal) { + return conversionUtil(decimal, { + fromNumericBase: 'dec', + toNumericBase: 'hex', + }); +} + +export function hexWEIToDecETH(hexWEI) { + return conversionUtil(hexWEI, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', + toDenomination: 'ETH', + }); +} diff --git a/shared/lib/ui-utils.js b/shared/lib/ui-utils.js new file mode 100644 index 000000000..e03ce61e7 --- /dev/null +++ b/shared/lib/ui-utils.js @@ -0,0 +1,7 @@ +let _supportLink = 'https://support.metamask.io'; + +///: BEGIN:ONLY_INCLUDE_IN(flask) +_supportLink = 'https://metamask-flask.zendesk.com/hc'; +///: END:ONLY_INCLUDE_IN + +export const SUPPORT_LINK = _supportLink; diff --git a/ui/components/app/account-menu/account-menu.component.js b/ui/components/app/account-menu/account-menu.component.js index fbd67f923..369927afc 100644 --- a/ui/components/app/account-menu/account-menu.component.js +++ b/ui/components/app/account-menu/account-menu.component.js @@ -16,7 +16,6 @@ import SiteIcon from '../../ui/site-icon'; import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'; import { PRIMARY, - SUPPORT_LINK, ///: BEGIN:ONLY_INCLUDE_IN(beta,flask) SUPPORT_REQUEST_LINK, ///: END:ONLY_INCLUDE_IN @@ -41,6 +40,7 @@ import IconImport from '../../ui/icon/icon-import'; import Button from '../../ui/button'; import SearchIcon from '../../ui/icon/search-icon'; +import { SUPPORT_LINK } from '../../../../shared/lib/ui-utils'; import KeyRingLabel from './keyring-label'; export function AccountMenuItem(props) { diff --git a/ui/components/app/asset-list/asset-list.test.js b/ui/components/app/asset-list/asset-list.test.js new file mode 100644 index 000000000..b3925d6b6 --- /dev/null +++ b/ui/components/app/asset-list/asset-list.test.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import AssetList from './asset-list'; + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + }, + }); + return renderWithProvider(, store); +}; + +describe('AssetList', () => { + it('renders AssetList component and shows Refresh List text', () => { + render(); + expect(screen.getByText('Refresh list')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js b/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js index d800a32ef..3e8293804 100644 --- a/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js +++ b/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js @@ -11,12 +11,9 @@ import mockEstimates from '../../../../test/data/mock-estimates.json'; import mockState from '../../../../test/data/mock-state.json'; import { GasFeeContextProvider } from '../../../contexts/gasFee'; import configureStore from '../../../store/store'; -import { - hexWEIToDecETH, - decGWEIToHexWEI, -} from '../../../helpers/utils/conversions.util'; +import { decGWEIToHexWEI } from '../../../helpers/utils/conversions.util'; import InfoTooltip from '../../ui/info-tooltip'; - +import { hexWEIToDecETH } from '../../../../shared/lib/transactions-controller-utils'; import CancelSpeedupPopover from './cancel-speedup-popover'; const MAXFEEPERGAS_ABOVE_MOCK_MEDIUM_HEX = '0x174876e800'; diff --git a/ui/components/app/create-new-vault/create-new-vault.test.js b/ui/components/app/create-new-vault/create-new-vault.test.js new file mode 100644 index 000000000..6f01804e0 --- /dev/null +++ b/ui/components/app/create-new-vault/create-new-vault.test.js @@ -0,0 +1,31 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import CreateNewVault from './create-new-vault'; + +const store = configureStore({ + metamask: { + ...mockState.metamask, + }, +}); + +describe('CreateNewVault', () => { + it('renders CreateNewVault component and shows Secret Recovery Phrase text', () => { + renderWithProvider(, store); + expect(screen.getByText('Secret Recovery Phrase')).toBeInTheDocument(); + }); + + it('renders CreateNewVault component and shows You can paste... text', () => { + renderWithProvider( + , + store, + ); + expect( + screen.getByText( + 'You can paste your entire secret recovery phrase into any field', + ), + ).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-item/useGasItemFeeDetails.js b/ui/components/app/edit-gas-fee-popover/edit-gas-item/useGasItemFeeDetails.js index 6db074514..303d72ffb 100644 --- a/ui/components/app/edit-gas-fee-popover/edit-gas-item/useGasItemFeeDetails.js +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-item/useGasItemFeeDetails.js @@ -6,17 +6,17 @@ import { PRIORITY_LEVELS, } from '../../../../../shared/constants/gas'; import { getMaximumGasTotalInHexWei } from '../../../../../shared/modules/gas.utils'; -import { - decGWEIToHexWEI, - decimalToHex, - hexWEIToDecGWEI, -} from '../../../../helpers/utils/conversions.util'; +import { decGWEIToHexWEI } from '../../../../helpers/utils/conversions.util'; import { addTenPercentAndRound, gasEstimateGreaterThanGasUsedPlusTenPercent, } from '../../../../helpers/utils/gas'; import { getAdvancedGasFeeValues } from '../../../../selectors'; import { useGasFeeContext } from '../../../../contexts/gasFee'; +import { + decimalToHex, + hexWEIToDecGWEI, +} from '../../../../../shared/lib/transactions-controller-utils'; import { useCustomTimeEstimate } from './useCustomTimeEstimate'; export const useGasItemFeeDetails = (priorityLevel) => { diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index cf4c21301..92cdd1b82 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -11,11 +11,7 @@ import { CUSTOM_GAS_ESTIMATE, } from '../../../../shared/constants/gas'; -import { - decGWEIToHexWEI, - decimalToHex, - hexToDecimal, -} from '../../../helpers/utils/conversions.util'; +import { decGWEIToHexWEI } from '../../../helpers/utils/conversions.util'; import Popover from '../../ui/popover'; import Button from '../../ui/button'; @@ -37,6 +33,8 @@ import LoadingHeartBeat from '../../ui/loading-heartbeat'; import { checkNetworkAndAccountSupports1559 } from '../../../selectors'; import { useIncrementedGasFees } from '../../../hooks/useIncrementedGasFees'; import { isLegacyTransaction } from '../../../helpers/utils/transactions.util'; +import { hexToDecimal } from '../../../../shared/lib/metamask-controller-utils'; +import { decimalToHex } from '../../../../shared/lib/transactions-controller-utils'; export default function EditGasPopover({ popoverTitle = '', diff --git a/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js b/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js index eca645f55..a800eefb3 100644 --- a/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js +++ b/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js @@ -1,11 +1,11 @@ import { connect } from 'react-redux'; +import { decGWEIToHexWEI } from '../../../../helpers/utils/conversions.util'; +import { getNetworkSupportsSettingGasFees } from '../../../../selectors/selectors'; +import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants'; import { - decGWEIToHexWEI, decimalToHex, hexWEIToDecGWEI, -} from '../../../../helpers/utils/conversions.util'; -import { getNetworkSupportsSettingGasFees } from '../../../../selectors/selectors'; -import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants'; +} from '../../../../../shared/lib/transactions-controller-utils'; import AdvancedGasInputs from './advanced-gas-inputs.component'; function convertGasPriceForInputs(gasPriceInHexWEI) { 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 e7126b6d3..ba80837b6 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 @@ -43,15 +43,11 @@ import { import { addHexes, subtractHexWEIsToDec, - hexWEIToDecGWEI, getValueFromWeiHex, sumHexWEIsToRenderableFiat, } from '../../../../helpers/utils/conversions.util'; import { formatETHFee } from '../../../../helpers/utils/formatters'; -import { - calcGasTotal, - isBalanceSufficient, -} from '../../../../pages/send/send.utils'; +import { isBalanceSufficient } from '../../../../pages/send/send.utils'; import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants'; import { ASSET_TYPES, @@ -59,6 +55,10 @@ import { } from '../../../../../shared/constants/transaction'; import { GAS_LIMITS } from '../../../../../shared/constants/gas'; import { updateGasFees } from '../../../../ducks/metamask/metamask'; +import { + calcGasTotal, + hexWEIToDecGWEI, +} from '../../../../../shared/lib/transactions-controller-utils'; import GasModalPageContainer from './gas-modal-page-container.component'; const mapStateToProps = (state, ownProps) => { diff --git a/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js b/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js index c95e9460a..bdc9acce5 100644 --- a/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js +++ b/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js @@ -6,7 +6,7 @@ import BigNumber from 'bignumber.js'; import Modal from '../../modal'; import Identicon from '../../../ui/identicon'; import TextField from '../../../ui/text-field'; -import { calcTokenAmount } from '../../../../helpers/utils/token-util'; +import { calcTokenAmount } from '../../../../../shared/lib/transactions-controller-utils'; const MAX_UNSIGNED_256_INT = new BigNumber(2).pow(256).minus(1).toString(10); diff --git a/ui/components/app/transaction-decoding/transaction-decoding.component.js b/ui/components/app/transaction-decoding/transaction-decoding.component.js index d1a73ca9d..dcedad335 100644 --- a/ui/components/app/transaction-decoding/transaction-decoding.component.js +++ b/ui/components/app/transaction-decoding/transaction-decoding.component.js @@ -6,11 +6,11 @@ import { useSelector } from 'react-redux'; import * as Codec from '@truffle/codec'; import Spinner from '../../ui/spinner'; import ErrorMessage from '../../ui/error-message'; -import fetchWithCache from '../../../helpers/utils/fetch-with-cache'; +import fetchWithCache from '../../../../shared/lib/fetch-with-cache'; import { getSelectedAccount, getCurrentChainId } from '../../../selectors'; -import { hexToDecimal } from '../../../helpers/utils/conversions.util'; import { I18nContext } from '../../../contexts/i18n'; import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils'; +import { hexToDecimal } from '../../../../shared/lib/metamask-controller-utils'; import { transformTxDecoding } from './transaction-decoding.util'; import { FETCH_PROJECT_INFO_URI, diff --git a/ui/components/app/transaction-list/transaction-list.test.js b/ui/components/app/transaction-list/transaction-list.test.js new file mode 100644 index 000000000..0f79aede7 --- /dev/null +++ b/ui/components/app/transaction-list/transaction-list.test.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import TransactionList from './transaction-list.component'; + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + }, + }); + return renderWithProvider(, store); +}; + +describe('TransactionList', () => { + it('renders TransactionList component and shows You have no transactions text', () => { + render(); + expect(screen.getByText('You have no transactions')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/whats-new-popup/whats-new-popup.test.js b/ui/components/app/whats-new-popup/whats-new-popup.test.js new file mode 100644 index 000000000..c92bdc600 --- /dev/null +++ b/ui/components/app/whats-new-popup/whats-new-popup.test.js @@ -0,0 +1,121 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import WhatsNewPopup from './whats-new-popup'; + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + announcements: { + 1: { + date: '2021-03-17', + id: 1, + image: { + height: '230px', + placeImageBelowDescription: true, + src: 'images/mobile-link-qr.svg', + width: '230px', + }, + isShown: false, + }, + 3: { + date: '2021-03-08', + id: 3, + isShown: false, + }, + 4: { + date: '2021-05-11', + id: 4, + image: { + src: 'images/source-logos-bsc.svg', + width: '100%', + }, + isShown: false, + }, + 5: { + date: '2021-06-09', + id: 5, + isShown: false, + }, + 6: { + date: '2021-05-26', + id: 6, + isShown: false, + }, + 7: { + date: '2021-09-17', + id: 7, + isShown: false, + }, + 8: { + date: '2021-11-01', + id: 8, + isShown: false, + }, + 9: { + date: '2021-12-07', + id: 9, + image: { + src: 'images/txinsights.png', + width: '80%', + }, + isShown: false, + }, + 10: { + date: '2022-04-18', + id: 10, + image: { + src: 'images/token-detection.svg', + width: '100%', + }, + isShown: true, + }, + 11: { + date: '2022-04-18', + id: 11, + isShown: true, + }, + 12: { + date: '2022-05-18', + id: 12, + image: { + src: 'images/darkmode-banner.png', + width: '100%', + }, + isShown: false, + }, + 13: { + date: '2022-07-12', + id: 13, + isShown: true, + }, + }, + }, + }); + return renderWithProvider(, store); +}; + +describe('WhatsNewPopup', () => { + beforeEach(() => { + const mockIntersectionObserver = jest.fn(); + mockIntersectionObserver.mockReturnValue({ + observe: () => null, + unobserve: () => null, + disconnect: () => null, + }); + window.IntersectionObserver = mockIntersectionObserver; + }); + + it("renders WhatsNewPopup component and shows What's new text", () => { + render(); + expect(screen.getByText("What's new")).toBeInTheDocument(); + }); + + it('renders WhatsNewPopup component and shows close button', () => { + render(); + expect(screen.getByTestId('popover-close')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/ui/account-list/account-list.test.js b/ui/components/ui/account-list/account-list.test.js new file mode 100644 index 000000000..00f02f11c --- /dev/null +++ b/ui/components/ui/account-list/account-list.test.js @@ -0,0 +1,60 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import AccountList from './account-list'; + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + }, + }); + + const args = { + accounts: [ + { + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + addressLabel: 'Account 1', + lastConnectedDate: 'Feb-22-2022', + balance: '8.7a73149c048545a3fe58', + has: () => { + /** nothing to do */ + }, + }, + ], + selectedAccounts: { + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + addressLabel: 'Account 2', + lastConnectedDate: 'Feb-22-2022', + balance: '8.7a73149c048545a3fe58', + has: () => { + /** nothing to do */ + }, + }, + addressLastConnectedMap: { + '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': 'Feb-22-2022', + }, + allAreSelected: () => true, + nativeCurrency: 'USD', + }; + return renderWithProvider(, store); +}; + +describe('AccountList', () => { + it('renders AccountList component and shows New account text', () => { + render(); + expect(screen.getByText('New account')).toBeInTheDocument(); + }); + + it('renders AccountList component and shows Account 1 text', () => { + render(); + expect(screen.getByText('Account 1')).toBeInTheDocument(); + }); + + it('renders AccountList component and shows ETH text', () => { + render(); + expect(screen.getByText('ETH')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/ui/hex-to-decimal/hex-to-decimal.component.js b/ui/components/ui/hex-to-decimal/hex-to-decimal.component.js index 762b7ccf0..290fc45cd 100644 --- a/ui/components/ui/hex-to-decimal/hex-to-decimal.component.js +++ b/ui/components/ui/hex-to-decimal/hex-to-decimal.component.js @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { hexToDecimal } from '../../../helpers/utils/conversions.util'; +import { hexToDecimal } from '../../../../shared/lib/metamask-controller-utils'; export default class HexToDecimal extends PureComponent { static propTypes = { diff --git a/ui/components/ui/new-network-info/new-network-info.js b/ui/components/ui/new-network-info/new-network-info.js index 79dcb8fe3..9b9b7e674 100644 --- a/ui/components/ui/new-network-info/new-network-info.js +++ b/ui/components/ui/new-network-info/new-network-info.js @@ -16,7 +16,7 @@ import { } from '../../../helpers/constants/design-system'; import Typography from '../typography'; import { TOKEN_API_METASWAP_CODEFI_URL } from '../../../../shared/constants/tokens'; -import fetchWithCache from '../../../helpers/utils/fetch-with-cache'; +import fetchWithCache from '../../../../shared/lib/fetch-with-cache'; import { getNativeCurrencyImage, getProvider, diff --git a/ui/components/ui/new-network-info/new-network-info.test.js b/ui/components/ui/new-network-info/new-network-info.test.js index 2c4e9359a..01fde0e8c 100644 --- a/ui/components/ui/new-network-info/new-network-info.test.js +++ b/ui/components/ui/new-network-info/new-network-info.test.js @@ -5,7 +5,7 @@ import { renderWithProvider } from '../../../../test/lib/render-helpers'; import NewNetworkInfo from './new-network-info'; const fetchWithCache = - require('../../../helpers/utils/fetch-with-cache').default; + require('../../../../shared/lib/fetch-with-cache').default; const state = { metamask: { diff --git a/ui/components/ui/nickname-popover/nickname-popover.test.js b/ui/components/ui/nickname-popover/nickname-popover.test.js new file mode 100644 index 000000000..d27c8eeee --- /dev/null +++ b/ui/components/ui/nickname-popover/nickname-popover.test.js @@ -0,0 +1,35 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import NicknamePopover from './nickname-popover.component'; + +const store = configureStore({ + metamask: { + ...mockState.metamask, + }, +}); + +describe('NicknamePopover', () => { + it('renders NicknamePopover component and shows Add a nickname text', () => { + renderWithProvider( + , + store, + ); + + expect(screen.getByText('Add a nickname')).toBeInTheDocument(); + }); + + it('renders NicknamePopover component and shows Edit nickname text', () => { + renderWithProvider( + , + store, + ); + + expect(screen.getByText('Edit nickname')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/ui/update-nickname-popover/update-nickname-popover.test.js b/ui/components/ui/update-nickname-popover/update-nickname-popover.test.js new file mode 100644 index 000000000..d03380694 --- /dev/null +++ b/ui/components/ui/update-nickname-popover/update-nickname-popover.test.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import UpdateNicknamePopover from './update-nickname-popover'; + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + }, + }); + return renderWithProvider( + , + store, + ); +}; + +describe('UpdateNicknamePopover', () => { + it('renders UpdateNicknamePopover component and shows This is a memo text', () => { + render(); + expect(screen.getByText('This is a memo')).toBeInTheDocument(); + }); +}); diff --git a/ui/ducks/send/helpers.js b/ui/ducks/send/helpers.js index 43d556c22..3e7a3bbf4 100644 --- a/ui/ducks/send/helpers.js +++ b/ui/ducks/send/helpers.js @@ -1,6 +1,7 @@ import { addHexPrefix } from 'ethereumjs-util'; import abi from 'human-standard-token-abi'; import { GAS_LIMITS, MIN_GAS_LIMIT_HEX } from '../../../shared/constants/gas'; +import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils'; import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network'; import { ASSET_TYPES, @@ -12,7 +13,6 @@ import { multiplyCurrencies, } from '../../../shared/modules/conversion.utils'; import { ETH, GWEI } from '../../helpers/constants/common'; -import { calcTokenAmount } from '../../helpers/utils/token-util'; import { addGasBuffer, generateERC20TransferData, diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index fa6b0a21c..810261749 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -22,7 +22,6 @@ import { } from '../../pages/send/send.constants'; import { - calcGasTotal, isBalanceSufficient, isTokenBalanceSufficient, } from '../../pages/send/send.utils'; @@ -67,9 +66,7 @@ import { GAS_FEE_ESTIMATES_UPDATED, } from '../../store/actionConstants'; import { - calcTokenAmount, getTokenAddressParam, - getTokenValueParam, getTokenMetadata, getTokenIdParam, } from '../../helpers/utils/token-util'; @@ -108,6 +105,11 @@ import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import { getValueFromWeiHex } from '../../helpers/utils/confirm-tx.util'; import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; +import { getTokenValueParam } from '../../../shared/lib/metamask-controller-utils'; +import { + calcGasTotal, + calcTokenAmount, +} from '../../../shared/lib/transactions-controller-utils'; import { estimateGasLimitForSend, generateTransactionParams, diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index 9641f6fe8..0cfc8ae76 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -51,16 +51,12 @@ import { parseSmartTransactionsError, stxErrorTypes, } from '../../pages/swaps/swaps.util'; -import { calcGasTotal } from '../../pages/send/send.utils'; import { - decimalToHex, getValueFromWeiHex, decGWEIToHexWEI, - hexWEIToDecGWEI, addHexes, } from '../../helpers/utils/conversions.util'; import { conversionLessThan } from '../../../shared/modules/conversion.utils'; -import { calcTokenAmount } from '../../helpers/utils/token-util'; import { getSelectedAccount, getTokenExchangeRates, @@ -89,6 +85,12 @@ import { } from '../../../shared/constants/transaction'; import { getGasFeeEstimates } from '../metamask/metamask'; import { ORIGIN_METAMASK } from '../../../shared/constants/app'; +import { + calcGasTotal, + calcTokenAmount, + decimalToHex, + hexWEIToDecGWEI, +} from '../../../shared/lib/transactions-controller-utils'; const GAS_PRICES_LOADING_STATES = { INITIAL: 'INITIAL', diff --git a/ui/ducks/swaps/swaps.test.js b/ui/ducks/swaps/swaps.test.js index d7142d3ca..22ab691a6 100644 --- a/ui/ducks/swaps/swaps.test.js +++ b/ui/ducks/swaps/swaps.test.js @@ -2,8 +2,8 @@ import nock from 'nock'; import { MOCKS, createSwapsMockStore } from '../../../test/jest'; import { setSwapsLiveness, setSwapsFeatureFlags } from '../../store/actions'; -import { setStorageItem } from '../../helpers/utils/storage-helpers'; import { CHAIN_IDS } from '../../../shared/constants/network'; +import { setStorageItem } from '../../../shared/lib/storage-helpers'; import * as swaps from './swaps'; jest.mock('../../store/actions.js', () => ({ diff --git a/ui/helpers/constants/common.js b/ui/helpers/constants/common.js index 335a50326..d0281dde6 100644 --- a/ui/helpers/constants/common.js +++ b/ui/helpers/constants/common.js @@ -12,17 +12,14 @@ export const GAS_ESTIMATE_TYPES = { FASTEST: 'FASTEST', }; -let _supportLink = 'https://support.metamask.io'; let _supportRequestLink = 'https://metamask.zendesk.com/hc/en-us'; const _contractAddressLink = 'https://metamask.zendesk.com/hc/en-us/articles/360020028092-What-is-the-known-contract-address-warning-'; ///: BEGIN:ONLY_INCLUDE_IN(flask) -_supportLink = 'https://metamask-flask.zendesk.com/hc'; _supportRequestLink = 'https://metamask-flask.zendesk.com/hc/en-us/requests/new'; ///: END:ONLY_INCLUDE_IN -export const SUPPORT_LINK = _supportLink; export const SUPPORT_REQUEST_LINK = _supportRequestLink; export const CONTRACT_ADDRESS_LINK = _contractAddressLink; diff --git a/ui/helpers/constants/critical-error.js b/ui/helpers/constants/critical-error.js deleted file mode 100644 index a9c18cf55..000000000 --- a/ui/helpers/constants/critical-error.js +++ /dev/null @@ -1 +0,0 @@ -export const TEN_SECONDS_IN_MILLISECONDS = 10_000; diff --git a/ui/helpers/constants/error-keys.js b/ui/helpers/constants/error-keys.js index de43f9ffc..7d3cb020f 100644 --- a/ui/helpers/constants/error-keys.js +++ b/ui/helpers/constants/error-keys.js @@ -1,7 +1,6 @@ export const INSUFFICIENT_FUNDS_ERROR_KEY = 'insufficientFunds'; export const GAS_LIMIT_TOO_LOW_ERROR_KEY = 'gasLimitTooLow'; export const TRANSACTION_ERROR_KEY = 'transactionError'; -export const TRANSACTION_NO_CONTRACT_ERROR_KEY = 'transactionErrorNoContract'; export const ETH_GAS_PRICE_FETCH_WARNING_KEY = 'ethGasPriceFetchWarning'; export const GAS_PRICE_FETCH_FAILURE_ERROR_KEY = 'gasPriceFetchFailed'; export const GAS_PRICE_EXCESSIVE_ERROR_KEY = 'gasPriceExcessive'; diff --git a/ui/helpers/constants/transactions.js b/ui/helpers/constants/transactions.js index 4dc66ab79..4bc5974cd 100644 --- a/ui/helpers/constants/transactions.js +++ b/ui/helpers/constants/transactions.js @@ -21,8 +21,3 @@ export const TOKEN_CATEGORY_HASH = { [TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER]: true, [TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM]: true, }; - -export const TRANSACTION_ENVELOPE_TYPE_NAMES = { - FEE_MARKET: 'fee-market', - LEGACY: 'legacy', -}; diff --git a/ui/helpers/utils/conversions.util.js b/ui/helpers/utils/conversions.util.js index fab3d5be9..18f189637 100644 --- a/ui/helpers/utils/conversions.util.js +++ b/ui/helpers/utils/conversions.util.js @@ -11,20 +11,6 @@ export function bnToHex(inputBn) { return addHexPrefix(inputBn.toString(16)); } -export function hexToDecimal(hexValue) { - return conversionUtil(hexValue, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - }); -} - -export function decimalToHex(decimal) { - return conversionUtil(decimal, { - fromNumericBase: 'dec', - toNumericBase: 'hex', - }); -} - export function getEthConversionFromWeiHex({ value, fromCurrency = ETH, @@ -135,15 +121,6 @@ export function decGWEIToHexWEI(decGWEI) { }); } -export function hexWEIToDecGWEI(decGWEI) { - return conversionUtil(decGWEI, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - fromDenomination: 'WEI', - toDenomination: 'GWEI', - }); -} - export function decETHToDecWEI(decEth) { return conversionUtil(decEth, { fromNumericBase: 'dec', diff --git a/ui/helpers/utils/gas.js b/ui/helpers/utils/gas.js index 56ca8f253..d9e3f3558 100644 --- a/ui/helpers/utils/gas.js +++ b/ui/helpers/utils/gas.js @@ -6,12 +6,12 @@ import { EDIT_GAS_MODES, } from '../../../shared/constants/gas'; import { multiplyCurrencies } from '../../../shared/modules/conversion.utils'; +import { hexWEIToDecGWEI } from '../../../shared/lib/transactions-controller-utils'; import { bnGreaterThan, isNullish, roundToDecimalPlacesRemovingExtraZeroes, } from './util'; -import { hexWEIToDecGWEI } from './conversions.util'; export const gasEstimateGreaterThanGasUsedPlusTenPercent = ( gasUsed, diff --git a/ui/helpers/utils/i18n-helper.js b/ui/helpers/utils/i18n-helper.js index aa80d6e55..593dd2b5c 100644 --- a/ui/helpers/utils/i18n-helper.js +++ b/ui/helpers/utils/i18n-helper.js @@ -2,7 +2,6 @@ import React from 'react'; import log from 'loglevel'; import * as Sentry from '@sentry/browser'; - import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'; const fetchWithTimeout = getFetchWithTimeout(); diff --git a/ui/helpers/utils/token-util.js b/ui/helpers/utils/token-util.js index c447fe998..3585d4fe9 100644 --- a/ui/helpers/utils/token-util.js +++ b/ui/helpers/utils/token-util.js @@ -1,5 +1,4 @@ import log from 'loglevel'; -import BigNumber from 'bignumber.js'; import { conversionUtil, multiplyCurrencies, @@ -8,6 +7,8 @@ import { getTokenStandardAndDetails } from '../../store/actions'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; import { ERC20 } from '../../../shared/constants/transaction'; +import { getTokenValueParam } from '../../../shared/lib/metamask-controller-utils'; +import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils'; import * as util from './util'; import { formatCurrency } from './confirm-tx.util'; @@ -108,16 +109,6 @@ export function tokenInfoGetter() { }; } -export function calcTokenAmount(value, decimals) { - const multiplier = Math.pow(10, Number(decimals || 0)); - return new BigNumber(String(value)).div(multiplier); -} - -export function calcTokenValue(value, decimals) { - const multiplier = Math.pow(10, Number(decimals || 0)); - return new BigNumber(String(value)).times(multiplier); -} - /** * Attempts to get the address parameter of the given token transaction data * (i.e. function call) per the Human Standard Token ABI, in the following @@ -141,10 +132,6 @@ export function getTokenAddressParam(tokenData = {}) { * @param {object} tokenData - ethers Interface token data. * @returns {string | undefined} A decimal string value. */ -export function getTokenValueParam(tokenData = {}) { - return tokenData?.args?._value?.toString(); -} - /** * Gets either the '_tokenId' parameter or the 'id' param of the passed token transaction data., * These are the parsed tokenId values returned by `parseStandardTokenTransactionData` as defined diff --git a/ui/helpers/utils/transactions.util.js b/ui/helpers/utils/transactions.util.js index f2b1ed628..b7c6a9def 100644 --- a/ui/helpers/utils/transactions.util.js +++ b/ui/helpers/utils/transactions.util.js @@ -10,7 +10,7 @@ import { } from '../../../shared/constants/transaction'; import { addCurrencies } from '../../../shared/modules/conversion.utils'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; -import fetchWithCache from './fetch-with-cache'; +import fetchWithCache from '../../../shared/lib/fetch-with-cache'; /** * @typedef EthersContractCall diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index e0c231d45..41b072b29 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -5,7 +5,6 @@ import * as ethUtil from 'ethereumjs-util'; import { DateTime } from 'luxon'; import { getFormattedIpfsUrl } from '@metamask/controllers/dist/util'; import slip44 from '@metamask/slip44'; -import { addHexPrefix } from '../../../app/scripts/lib/util'; import { CHAIN_IDS } from '../../../shared/constants/network'; import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; import { @@ -293,71 +292,6 @@ export function checkExistingAddresses(address, list = []) { return list.some(matchesAddress); } -/** - * Given a number and specified precision, returns that number in base 10 with a maximum of precision - * significant digits, but without any trailing zeros after the decimal point To be used when wishing - * to display only as much digits to the user as necessary - * - * @param {string | number | BigNumber} n - The number to format - * @param {number} precision - The maximum number of significant digits in the return value - * @returns {string} The number in decimal form, with <= precision significant digits and no decimal trailing zeros - */ -export function toPrecisionWithoutTrailingZeros(n, precision) { - return new BigNumber(n) - .toPrecision(precision) - .replace(/(\.[0-9]*[1-9])0*|(\.0*)/u, '$1'); -} - -/** - * Given and object where all values are strings, returns the same object with all values - * now prefixed with '0x' - * - * @param obj - */ -export function addHexPrefixToObjectValues(obj) { - return Object.keys(obj).reduce((newObj, key) => { - return { ...newObj, [key]: addHexPrefix(obj[key]) }; - }, {}); -} - -/** - * Given the standard set of information about a transaction, returns a transaction properly formatted for - * publishing via JSON RPC and web3 - * - * @param {object} options - * @param {boolean} [options.sendToken] - Indicates whether or not the transaciton is a token transaction - * @param {string} options.data - A hex string containing the data to include in the transaction - * @param {string} options.to - A hex address of the tx recipient address - * @param options.amount - * @param {string} options.from - A hex address of the tx sender address - * @param {string} options.gas - A hex representation of the gas value for the transaction - * @param {string} options.gasPrice - A hex representation of the gas price for the transaction - * @returns {object} An object ready for submission to the blockchain, with all values appropriately hex prefixed - */ -export function constructTxParams({ - sendToken, - data, - to, - amount, - from, - gas, - gasPrice, -}) { - const txParams = { - data, - from, - value: '0', - gas, - gasPrice, - }; - - if (!sendToken) { - txParams.value = amount; - txParams.to = to; - } - return addHexPrefixToObjectValues(txParams); -} - export function bnGreaterThan(a, b) { if (a === null || a === undefined || b === null || b === undefined) { return null; diff --git a/ui/helpers/utils/util.test.js b/ui/helpers/utils/util.test.js index 4dd6ab632..51893f783 100644 --- a/ui/helpers/utils/util.test.js +++ b/ui/helpers/utils/util.test.js @@ -1,4 +1,6 @@ import { BN } from 'ethereumjs-util'; +import { addHexPrefixToObjectValues } from '../../../shared/lib/swaps-utils'; +import { toPrecisionWithoutTrailingZeros } from '../../../shared/lib/transactions-controller-utils'; import * as util from './util'; describe('util', () => { @@ -279,9 +281,7 @@ describe('util', () => { testData.forEach(({ args, result }) => { it(`should return ${result} when passed number ${args[0]} and precision ${args[1]}`, () => { - expect(util.toPrecisionWithoutTrailingZeros(...args)).toStrictEqual( - result, - ); + expect(toPrecisionWithoutTrailingZeros(...args)).toStrictEqual(result); }); }); }); @@ -289,7 +289,7 @@ describe('util', () => { describe('addHexPrefixToObjectValues()', () => { it('should return a new object with the same properties with a 0x prefix', () => { expect( - util.addHexPrefixToObjectValues({ + addHexPrefixToObjectValues({ prop1: '0x123', prop2: '456', prop3: 'x', diff --git a/ui/hooks/gasFeeInput/useGasEstimates.js b/ui/hooks/gasFeeInput/useGasEstimates.js index 351401954..25317e265 100644 --- a/ui/hooks/gasFeeInput/useGasEstimates.js +++ b/ui/hooks/gasFeeInput/useGasEstimates.js @@ -14,14 +14,12 @@ import { checkNetworkAndAccountSupports1559, getShouldShowFiat, } from '../../selectors'; -import { - decGWEIToHexWEI, - decimalToHex, -} from '../../helpers/utils/conversions.util'; +import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; import { useCurrencyDisplay } from '../useCurrencyDisplay'; import { useUserPreferencedCurrency } from '../useUserPreferencedCurrency'; +import { decimalToHex } from '../../../shared/lib/transactions-controller-utils'; /** * @typedef {object} GasEstimatesReturnType diff --git a/ui/hooks/gasFeeInput/useGasEstimates.test.js b/ui/hooks/gasFeeInput/useGasEstimates.test.js index 4826adbd2..894cf00af 100644 --- a/ui/hooks/gasFeeInput/useGasEstimates.test.js +++ b/ui/hooks/gasFeeInput/useGasEstimates.test.js @@ -4,11 +4,8 @@ import { getMaximumGasTotalInHexWei, getMinimumGasTotalInHexWei, } from '../../../shared/modules/gas.utils'; -import { - decGWEIToHexWEI, - decimalToHex, -} from '../../helpers/utils/conversions.util'; - +import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; +import { decimalToHex } from '../../../shared/lib/transactions-controller-utils'; import { FEE_MARKET_ESTIMATE_RETURN_VALUE, LEGACY_GAS_ESTIMATE_RETURN_VALUE, diff --git a/ui/hooks/gasFeeInput/useGasFeeInputs.js b/ui/hooks/gasFeeInput/useGasFeeInputs.js index 1b1840c22..91bbbb678 100644 --- a/ui/hooks/gasFeeInput/useGasFeeInputs.js +++ b/ui/hooks/gasFeeInput/useGasFeeInputs.js @@ -13,11 +13,11 @@ import { getAdvancedInlineGasShown, getEIP1559V2Enabled, } from '../../selectors'; -import { hexToDecimal } from '../../helpers/utils/conversions.util'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; import { useGasFeeEstimates } from '../useGasFeeEstimates'; import { editGasModeIsSpeedUpOrCancel } from '../../helpers/utils/gas'; +import { hexToDecimal } from '../../../shared/lib/metamask-controller-utils'; import { useGasFeeErrors } from './useGasFeeErrors'; import { useGasPriceInput } from './useGasPriceInput'; import { useMaxFeePerGasInput } from './useMaxFeePerGasInput'; diff --git a/ui/hooks/gasFeeInput/useGasPriceInput.js b/ui/hooks/gasFeeInput/useGasPriceInput.js index ca27f104b..a931c0ff9 100644 --- a/ui/hooks/gasFeeInput/useGasPriceInput.js +++ b/ui/hooks/gasFeeInput/useGasPriceInput.js @@ -5,9 +5,9 @@ import { GAS_ESTIMATE_TYPES, CUSTOM_GAS_ESTIMATE, } from '../../../shared/constants/gas'; -import { hexWEIToDecGWEI } from '../../helpers/utils/conversions.util'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; +import { hexWEIToDecGWEI } from '../../../shared/lib/transactions-controller-utils'; import { feeParamsAreCustom } from './utils'; function getGasPriceEstimate(gasFeeEstimates, gasEstimateType, estimateToUse) { diff --git a/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js b/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js index 08c7afec4..5bf672663 100644 --- a/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js +++ b/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js @@ -4,11 +4,7 @@ import { useSelector } from 'react-redux'; import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas'; import { SECONDARY } from '../../helpers/constants/common'; import { getMaximumGasTotalInHexWei } from '../../../shared/modules/gas.utils'; -import { - decGWEIToHexWEI, - decimalToHex, - hexWEIToDecGWEI, -} from '../../helpers/utils/conversions.util'; +import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; import { checkNetworkAndAccountSupports1559, getShouldShowFiat, @@ -17,6 +13,10 @@ import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; import { useCurrencyDisplay } from '../useCurrencyDisplay'; import { useUserPreferencedCurrency } from '../useUserPreferencedCurrency'; +import { + decimalToHex, + hexWEIToDecGWEI, +} from '../../../shared/lib/transactions-controller-utils'; import { feeParamsAreCustom, getGasFeeEstimate } from './utils'; const getMaxFeePerGasFromTransaction = (transaction, gasFeeEstimates) => { diff --git a/ui/hooks/gasFeeInput/useMaxFeePerGasInput.test.js b/ui/hooks/gasFeeInput/useMaxFeePerGasInput.test.js index 318e9d7e9..f2312a0f4 100644 --- a/ui/hooks/gasFeeInput/useMaxFeePerGasInput.test.js +++ b/ui/hooks/gasFeeInput/useMaxFeePerGasInput.test.js @@ -2,12 +2,12 @@ import { useSelector } from 'react-redux'; import { act, renderHook } from '@testing-library/react-hooks'; import { getMaximumGasTotalInHexWei } from '../../../shared/modules/gas.utils'; -import { decimalToHex } from '../../helpers/utils/conversions.util'; import { GAS_RECOMMENDATIONS, CUSTOM_GAS_ESTIMATE, } from '../../../shared/constants/gas'; +import { decimalToHex } from '../../../shared/lib/transactions-controller-utils'; import { FEE_MARKET_ESTIMATE_RETURN_VALUE, LEGACY_GAS_ESTIMATE_RETURN_VALUE, diff --git a/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js b/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js index 3d3104866..e2ae55047 100644 --- a/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js +++ b/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js @@ -4,7 +4,6 @@ import { useEffect, useState } from 'react'; import { addHexPrefix } from 'ethereumjs-util'; import { SECONDARY } from '../../helpers/constants/common'; -import { hexWEIToDecGWEI } from '../../helpers/utils/conversions.util'; import { checkNetworkAndAccountSupports1559, getShouldShowFiat, @@ -14,6 +13,7 @@ import { multiplyCurrencies } from '../../../shared/modules/conversion.utils'; import { useCurrencyDisplay } from '../useCurrencyDisplay'; import { useUserPreferencedCurrency } from '../useUserPreferencedCurrency'; +import { hexWEIToDecGWEI } from '../../../shared/lib/transactions-controller-utils'; import { feeParamsAreCustom, getGasFeeEstimate } from './utils'; const getMaxPriorityFeePerGasFromTransaction = ( diff --git a/ui/hooks/gasFeeInput/useTransactionFunctions.js b/ui/hooks/gasFeeInput/useTransactionFunctions.js index 107dd076d..4219a9510 100644 --- a/ui/hooks/gasFeeInput/useTransactionFunctions.js +++ b/ui/hooks/gasFeeInput/useTransactionFunctions.js @@ -1,11 +1,9 @@ import { useCallback } from 'react'; import { useDispatch } from 'react-redux'; +import { decimalToHex } from '../../../shared/lib/transactions-controller-utils'; import { EDIT_GAS_MODES, PRIORITY_LEVELS } from '../../../shared/constants/gas'; -import { - decimalToHex, - decGWEIToHexWEI, -} from '../../helpers/utils/conversions.util'; +import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; import { addTenPercentAndRound, editGasModeIsSpeedUpOrCancel, diff --git a/ui/hooks/useSwappedTokenValue.js b/ui/hooks/useSwappedTokenValue.js index 581f66bca..c5d50817e 100644 --- a/ui/hooks/useSwappedTokenValue.js +++ b/ui/hooks/useSwappedTokenValue.js @@ -1,10 +1,10 @@ import { useSelector } from 'react-redux'; +import { getSwapsTokensReceivedFromTxMeta } from '../../shared/lib/transactions-controller-utils'; import { TRANSACTION_TYPES } from '../../shared/constants/transaction'; import { isSwapsDefaultTokenAddress, isSwapsDefaultTokenSymbol, } from '../../shared/modules/swaps.utils'; -import { getSwapsTokensReceivedFromTxMeta } from '../pages/swaps/swaps.util'; import { getCurrentChainId } from '../selectors'; import { useTokenFiatAmount } from './useTokenFiatAmount'; diff --git a/ui/hooks/useTokenDisplayValue.js b/ui/hooks/useTokenDisplayValue.js index 5b60428a9..bdeae9c0e 100644 --- a/ui/hooks/useTokenDisplayValue.js +++ b/ui/hooks/useTokenDisplayValue.js @@ -1,8 +1,6 @@ import { useMemo } from 'react'; -import { - getTokenValueParam, - calcTokenAmount, -} from '../helpers/utils/token-util'; +import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils'; +import { calcTokenAmount } from '../../shared/lib/transactions-controller-utils'; import { useTokenData } from './useTokenData'; /** diff --git a/ui/hooks/useTokenDisplayValue.test.js b/ui/hooks/useTokenDisplayValue.test.js index 26e2a4374..76834c1ea 100644 --- a/ui/hooks/useTokenDisplayValue.test.js +++ b/ui/hooks/useTokenDisplayValue.test.js @@ -1,7 +1,7 @@ import { renderHook } from '@testing-library/react-hooks'; import sinon from 'sinon'; -import * as tokenUtil from '../helpers/utils/token-util'; import * as txUtil from '../../shared/modules/transaction.utils'; +import * as metamaskControllerUtils from '../../shared/lib/metamask-controller-utils'; import { useTokenDisplayValue } from './useTokenDisplayValue'; const tests = [ @@ -121,7 +121,10 @@ describe('useTokenDisplayValue', () => { tests.forEach(({ displayValue, token, tokenData, tokenValue }, idx) => { describe(`when input is decimals: ${token.decimals} and value: ${tokenValue}`, () => { it(`should return ${displayValue} as displayValue`, () => { - const getTokenValueStub = sinon.stub(tokenUtil, 'getTokenValueParam'); + const getTokenValueStub = sinon.stub( + metamaskControllerUtils, + 'getTokenValueParam', + ); const parseStandardTokenTransactionDataStub = sinon.stub( txUtil, 'parseStandardTokenTransactionData', diff --git a/ui/hooks/useTransactionDisplayData.js b/ui/hooks/useTransactionDisplayData.js index d77da473f..c9e269e91 100644 --- a/ui/hooks/useTransactionDisplayData.js +++ b/ui/hooks/useTransactionDisplayData.js @@ -9,7 +9,6 @@ import { PRIMARY, SECONDARY } from '../helpers/constants/common'; import { getTokenAddressParam, getTokenIdParam, - getTokenValueParam, } from '../helpers/utils/token-util'; import { formatDateWithYearContext, @@ -29,6 +28,7 @@ import { } from '../../shared/constants/transaction'; import { captureSingleException } from '../store/actions'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; +import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils'; import { useI18nContext } from './useI18nContext'; import { useTokenFiatAmount } from './useTokenFiatAmount'; import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'; diff --git a/ui/index.js b/ui/index.js index 5cac3b9b9..0d7a09e31 100644 --- a/ui/index.js +++ b/ui/index.js @@ -1,6 +1,6 @@ import copyToClipboard from 'copy-to-clipboard'; import log from 'loglevel'; -import { clone, memoize } from 'lodash'; +import { clone } from 'lodash'; import React from 'react'; import { render } from 'react-dom'; import browser from 'webextension-polyfill'; @@ -10,13 +10,10 @@ import { ALERT_TYPES } from '../shared/constants/alerts'; import { maskObject } from '../shared/modules/object.utils'; import { SENTRY_STATE } from '../app/scripts/lib/setupSentry'; import { ENVIRONMENT_TYPE_POPUP } from '../shared/constants/app'; +import switchDirection from '../shared/lib/switch-direction'; +import { setupLocale } from '../shared/lib/error-utils'; import * as actions from './store/actions'; import configureStore from './store/store'; -import { - fetchLocale, - loadRelativeTimeFormatLocaleData, -} from './helpers/utils/i18n-helper'; -import switchDirection from './helpers/utils/switch-direction'; import { getPermittedAccountsForCurrentTab, getSelectedAddress, @@ -69,22 +66,6 @@ export default function launchMetamaskUi(opts, cb) { }); } -const _setupLocale = async (currentLocale) => { - const currentLocaleMessages = currentLocale - ? await fetchLocale(currentLocale) - : {}; - const enLocaleMessages = await fetchLocale('en'); - - await loadRelativeTimeFormatLocaleData('en'); - if (currentLocale) { - await loadRelativeTimeFormatLocaleData(currentLocale); - } - - return { currentLocaleMessages, enLocaleMessages }; -}; - -export const setupLocale = memoize(_setupLocale); - async function startApp(metamaskState, backgroundConnection, opts) { // parse opts if (!metamaskState.featureFlags) { diff --git a/ui/index.test.js b/ui/index.test.js index 9cb44c025..16dd70d96 100644 --- a/ui/index.test.js +++ b/ui/index.test.js @@ -1,4 +1,4 @@ -import { setupLocale } from '.'; +import { setupLocale } from '../shared/lib/error-utils'; const enMessages = { troubleStarting: { diff --git a/ui/pages/confirm-approve/confirm-approve.js b/ui/pages/confirm-approve/confirm-approve.js index 59bac6bc1..af2c694fe 100644 --- a/ui/pages/confirm-approve/confirm-approve.js +++ b/ui/pages/confirm-approve/confirm-approve.js @@ -8,10 +8,7 @@ import { updateCustomNonce, getNextNonce, } from '../../store/actions'; -import { - calcTokenAmount, - getTokenApprovedParam, -} from '../../helpers/utils/token-util'; +import { getTokenApprovedParam } from '../../helpers/utils/token-util'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; import { GasFeeContextProvider } from '../../contexts/gasFee'; import { TransactionModalContextProvider } from '../../contexts/transaction-modal'; @@ -38,6 +35,7 @@ import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popov import Loading from '../../components/ui/loading-screen'; import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; import { ERC1155, ERC20, ERC721 } from '../../../shared/constants/transaction'; +import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils'; import { getCustomTxParamsData } from './confirm-approve.util'; import ConfirmApproveContent from './confirm-approve-content'; diff --git a/ui/pages/confirm-approve/confirm-approve.util.js b/ui/pages/confirm-approve/confirm-approve.util.js index 022dc108a..e54ea573d 100644 --- a/ui/pages/confirm-approve/confirm-approve.util.js +++ b/ui/pages/confirm-approve/confirm-approve.util.js @@ -1,10 +1,8 @@ +import { calcTokenValue } from '../../../shared/lib/swaps-utils'; +import { decimalToHex } from '../../../shared/lib/transactions-controller-utils'; import { TRANSACTION_TYPES } from '../../../shared/constants/transaction'; import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; -import { decimalToHex } from '../../helpers/utils/conversions.util'; -import { - calcTokenValue, - getTokenAddressParam, -} from '../../helpers/utils/token-util'; +import { getTokenAddressParam } from '../../helpers/utils/token-util'; export function getCustomTxParamsData( data, diff --git a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js index 735ede094..552635435 100644 --- a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js +++ b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js @@ -11,11 +11,8 @@ import { addFiat, roundExponential, } from '../../helpers/utils/confirm-tx.util'; -import { - getWeiHexFromDecimalValue, - hexWEIToDecETH, -} from '../../helpers/utils/conversions.util'; import { ETH, PRIMARY } from '../../helpers/constants/common'; +import { getWeiHexFromDecimalValue } from '../../helpers/utils/conversions.util'; import { contractExchangeRateSelector, getCurrentCurrency, @@ -25,6 +22,7 @@ import { getNativeCurrency, } from '../../ducks/metamask/metamask'; import { ERC1155, ERC20, ERC721 } from '../../../shared/constants/transaction'; +import { hexWEIToDecETH } from '../../../shared/lib/transactions-controller-utils'; export default function ConfirmTokenTransactionBase({ image = '', diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index c76d5ba11..75e2599d6 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -3,11 +3,7 @@ import PropTypes from 'prop-types'; import ConfirmPageContainer from '../../components/app/confirm-page-container'; import TransactionDecoding from '../../components/app/transaction-decoding'; import { isBalanceSufficient } from '../send/send.utils'; -import { - addHexes, - hexToDecimal, - hexWEIToDecGWEI, -} from '../../helpers/utils/conversions.util'; +import { addHexes } from '../../helpers/utils/conversions.util'; import { CONFIRM_TRANSACTION_ROUTE, DEFAULT_ROUTE, @@ -63,6 +59,8 @@ import Typography from '../../components/ui/typography/typography'; import { MIN_GAS_LIMIT_DEC } from '../send/send.constants'; import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network'; +import { hexToDecimal } from '../../../shared/lib/metamask-controller-utils'; +import { hexWEIToDecGWEI } from '../../../shared/lib/transactions-controller-utils'; import TransactionAlerts from './transaction-alerts'; const renderHeartBeatIfNotInTest = () => 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 78770daa2..1599d759b 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -14,7 +14,7 @@ import { tryReverseResolveAddress, setDefaultHomeActiveTabName, } from '../../store/actions'; -import { isBalanceSufficient, calcGasTotal } from '../send/send.utils'; +import { isBalanceSufficient } from '../send/send.utils'; import { shortenAddress, valuesFor } from '../../helpers/utils/util'; import { getAdvancedInlineGasShown, @@ -57,6 +57,7 @@ import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas'; import { TRANSACTION_TYPES } from '../../../shared/constants/transaction'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import { getTokenAddressParam } from '../../helpers/utils/token-util'; +import { calcGasTotal } from '../../../shared/lib/transactions-controller-utils'; import ConfirmTransactionBase from './confirm-transaction-base.component'; let customNonceValue = ''; diff --git a/ui/pages/confirmation/templates/add-ethereum-chain.js b/ui/pages/confirmation/templates/add-ethereum-chain.js index 595f38c31..fbdae323e 100644 --- a/ui/pages/confirmation/templates/add-ethereum-chain.js +++ b/ui/pages/confirmation/templates/add-ethereum-chain.js @@ -12,9 +12,8 @@ import { ALIGN_ITEMS, } from '../../../helpers/constants/design-system'; import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'; - -import fetchWithCache from '../../../helpers/utils/fetch-with-cache'; import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; +import fetchWithCache from '../../../../shared/lib/fetch-with-cache'; const UNRECOGNIZED_CHAIN = { id: 'UNRECOGNIZED_CHAIN', diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index ccd454eed..31111e3d3 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -1,9 +1,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { Redirect, Route } from 'react-router-dom'; -///: BEGIN:ONLY_INCLUDE_IN(main) -import { SUPPORT_LINK } from '../../helpers/constants/common'; -///: END:ONLY_INCLUDE_IN import { EVENT, EVENT_NAMES, @@ -52,6 +49,9 @@ import { } from '../../helpers/constants/routes'; import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; import Tooltip from '../../components/ui/tooltip'; +///: BEGIN:ONLY_INCLUDE_IN(main) +import { SUPPORT_LINK } from '../../../shared/lib/ui-utils'; +///: END:ONLY_INCLUDE_IN ///: BEGIN:ONLY_INCLUDE_IN(beta) import BetaHomeFooter from './beta/beta-home-footer.component'; ///: END:ONLY_INCLUDE_IN diff --git a/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js b/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js index 236ed1655..0f25ed5ac 100644 --- a/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js +++ b/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js @@ -24,7 +24,7 @@ import { setCustomGasLimit, } from '../../../../ducks/gas/gas.duck'; import { showModal } from '../../../../store/actions'; -import { hexToDecimal } from '../../../../helpers/utils/conversions.util'; +import { hexToDecimal } from '../../../../../shared/lib/metamask-controller-utils'; import SendGasRow from './send-gas-row.component'; export default connect( diff --git a/ui/pages/send/send-content/send-gas-row/send-gas-row.stories.js b/ui/pages/send/send-content/send-gas-row/send-gas-row.stories.js index 27281ae66..7d33033f2 100644 --- a/ui/pages/send/send-content/send-gas-row/send-gas-row.stories.js +++ b/ui/pages/send/send-content/send-gas-row/send-gas-row.stories.js @@ -3,10 +3,10 @@ import React, { useEffect, useState } from 'react'; import { Provider } from 'react-redux'; import testData from '../../../../../.storybook/test-data'; +import { calcGasTotal } from '../../../../../shared/lib/transactions-controller-utils'; import { GAS_INPUT_MODES } from '../../../../ducks/send'; import { updateMetamaskState } from '../../../../store/actions'; import configureStore from '../../../../store/store'; -import { calcGasTotal } from '../../send.utils'; import README from './README.mdx'; import SendGasRow from './send-gas-row.component'; diff --git a/ui/pages/send/send.utils.js b/ui/pages/send/send.utils.js index 2a917dcd1..1a0450aa8 100644 --- a/ui/pages/send/send.utils.js +++ b/ui/pages/send/send.utils.js @@ -8,9 +8,9 @@ import { conversionLessThan, } from '../../../shared/modules/conversion.utils'; -import { calcTokenAmount } from '../../helpers/utils/token-util'; import { addHexPrefix } from '../../../app/scripts/lib/util'; import { ERC20, ERC721 } from '../../../shared/constants/transaction'; +import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils'; import { TOKEN_TRANSFER_FUNCTION_SIGNATURE, COLLECTIBLE_TRANSFER_FROM_FUNCTION_SIGNATURE, @@ -18,7 +18,6 @@ import { export { addGasBuffer, - calcGasTotal, getAssetTransferData, generateERC20TransferData, generateERC721TransferData, @@ -27,14 +26,6 @@ export { ellipsify, }; -function calcGasTotal(gasLimit = '0', gasPrice = '0') { - return multiplyCurrencies(gasLimit, gasPrice, { - toNumericBase: 'hex', - multiplicandBase: 16, - multiplierBase: 16, - }); -} - function isBalanceSufficient({ amount = '0x0', balance = '0x0', diff --git a/ui/pages/send/send.utils.test.js b/ui/pages/send/send.utils.test.js index 39f7c7b8f..34e6bbc61 100644 --- a/ui/pages/send/send.utils.test.js +++ b/ui/pages/send/send.utils.test.js @@ -1,4 +1,5 @@ import { rawEncode } from 'ethereumjs-abi'; +import { calcGasTotal } from '../../../shared/lib/transactions-controller-utils'; import { multiplyCurrencies, @@ -8,7 +9,6 @@ import { } from '../../../shared/modules/conversion.utils'; import { - calcGasTotal, generateERC20TransferData, isBalanceSufficient, isTokenBalanceSufficient, @@ -32,9 +32,16 @@ jest.mock('../../../shared/modules/conversion.utils', () => ({ conversionLessThan: (obj1, obj2) => obj1.value < obj2.value, })); -jest.mock('../../helpers/utils/token-util', () => ({ - calcTokenAmount: (a, d) => `calc:${a}${d}`, -})); +jest.mock('../../../shared/lib/transactions-controller-utils', () => { + const originalModule = jest.requireActual( + '../../../shared/lib/transactions-controller-utils', + ); + + return { + ...originalModule, + calcTokenAmount: (a, d) => `calc:${a}${d}`, + }; +}); jest.mock('ethereumjs-abi', () => ({ rawEncode: jest.fn().mockReturnValue(16, 1100), diff --git a/ui/pages/settings/experimental-tab/experimental-tab.test.js b/ui/pages/settings/experimental-tab/experimental-tab.test.js new file mode 100644 index 000000000..bb057fff0 --- /dev/null +++ b/ui/pages/settings/experimental-tab/experimental-tab.test.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import ExperimentalTab from './experimental-tab.component'; + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + }, + }); + return renderWithProvider(, store); +}; + +describe('ExperimentalTab', () => { + it('renders ExperimentalTab component and shows Enable enhanced gas fee UI text', () => { + render(); + expect(screen.getByText('Enable enhanced gas fee UI')).toBeInTheDocument(); + }); +}); diff --git a/ui/pages/settings/info-tab/info-tab.component.js b/ui/pages/settings/info-tab/info-tab.component.js index d842b4f01..514030da8 100644 --- a/ui/pages/settings/info-tab/info-tab.component.js +++ b/ui/pages/settings/info-tab/info-tab.component.js @@ -2,10 +2,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import Button from '../../../components/ui/button'; -import { - SUPPORT_LINK, - SUPPORT_REQUEST_LINK, -} from '../../../helpers/constants/common'; +import { SUPPORT_REQUEST_LINK } from '../../../helpers/constants/common'; import { isBeta } from '../../../helpers/utils/build-types'; import { getNumberOfSettingsInSection, @@ -16,6 +13,7 @@ import { EVENT_NAMES, CONTEXT_PROPS, } from '../../../../shared/constants/metametrics'; +import { SUPPORT_LINK } from '../../../../shared/lib/ui-utils'; export default class InfoTab extends PureComponent { state = { diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js index 06ab71128..b3e79e5d6 100644 --- a/ui/pages/settings/networks-tab/networks-form/networks-form.js +++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js @@ -22,7 +22,6 @@ import { jsonRpcRequest } from '../../../../../shared/modules/rpc.utils'; import ActionableMessage from '../../../../components/ui/actionable-message'; import Button from '../../../../components/ui/button'; import FormField from '../../../../components/ui/form-field'; -import { decimalToHex } from '../../../../helpers/utils/conversions.util'; import { setSelectedSettingsRpcUrl, updateAndSetCustomRpc, @@ -34,7 +33,7 @@ import { DEFAULT_ROUTE, NETWORKS_ROUTE, } from '../../../../helpers/constants/routes'; -import fetchWithCache from '../../../../helpers/utils/fetch-with-cache'; +import fetchWithCache from '../../../../../shared/lib/fetch-with-cache'; import { usePrevious } from '../../../../hooks/usePrevious'; import { MetaMetricsContext } from '../../../../contexts/metametrics'; import { EVENT } from '../../../../../shared/constants/metametrics'; @@ -42,6 +41,7 @@ import { infuraProjectId, FEATURED_RPCS, } from '../../../../../shared/constants/network'; +import { decimalToHex } from '../../../../../shared/lib/transactions-controller-utils'; /** * Attempts to convert the given chainId to a decimal string, for display diff --git a/ui/pages/swaps/awaiting-swap/awaiting-swap.js b/ui/pages/swaps/awaiting-swap/awaiting-swap.js index 52e0fc00a..5db7ce571 100644 --- a/ui/pages/swaps/awaiting-swap/awaiting-swap.js +++ b/ui/pages/swaps/awaiting-swap/awaiting-swap.js @@ -6,7 +6,6 @@ import { useHistory } from 'react-router-dom'; import isEqual from 'lodash/isEqual'; import { getBlockExplorerLink } from '@metamask/etherscan-link'; import { I18nContext } from '../../../contexts/i18n'; -import { SUPPORT_LINK } from '../../../helpers/constants/common'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { EVENT, @@ -62,6 +61,7 @@ import SwapsFooter from '../swaps-footer'; import CreateNewSwap from '../create-new-swap'; import ViewOnBlockExplorer from '../view-on-block-explorer'; +import { SUPPORT_LINK } from '../../../../shared/lib/ui-utils'; import SwapFailureIcon from './swap-failure-icon'; import SwapSuccessIcon from './swap-success-icon'; import QuotesTimeoutIcon from './quotes-timeout-icon'; diff --git a/ui/pages/swaps/build-quote/build-quote.js b/ui/pages/swaps/build-quote/build-quote.js index 9317caec3..9171037fe 100644 --- a/ui/pages/swaps/build-quote/build-quote.js +++ b/ui/pages/swaps/build-quote/build-quote.js @@ -71,11 +71,7 @@ import { getHardwareWalletType, } from '../../../selectors'; -import { - getValueFromWeiHex, - hexToDecimal, -} from '../../../helpers/utils/conversions.util'; -import { calcTokenAmount } from '../../../helpers/utils/token-util'; +import { getValueFromWeiHex } from '../../../helpers/utils/conversions.util'; import { getURLHostName } from '../../../helpers/utils/util'; import { usePrevious } from '../../../hooks/usePrevious'; import { useTokenTracker } from '../../../hooks/useTokenTracker'; @@ -106,10 +102,12 @@ import { countDecimals, fetchTokenPrice, fetchTokenBalance, - shouldEnableDirectWrapping, } from '../swaps.util'; import SwapsFooter from '../swaps-footer'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; +import { hexToDecimal } from '../../../../shared/lib/metamask-controller-utils'; +import { calcTokenAmount } from '../../../../shared/lib/transactions-controller-utils'; +import { shouldEnableDirectWrapping } from '../../../../shared/lib/swaps-utils'; const fuseSearchKeys = [ { name: 'name', weight: 0.499 }, diff --git a/ui/pages/swaps/exchange-rate-display/exchange-rate-display.js b/ui/pages/swaps/exchange-rate-display/exchange-rate-display.js index e9eb34c6c..87de9fd6d 100644 --- a/ui/pages/swaps/exchange-rate-display/exchange-rate-display.js +++ b/ui/pages/swaps/exchange-rate-display/exchange-rate-display.js @@ -2,8 +2,8 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import BigNumber from 'bignumber.js'; import classnames from 'classnames'; -import { calcTokenAmount } from '../../../helpers/utils/token-util'; import { formatSwapsValueForDisplay } from '../swaps.util'; +import { calcTokenAmount } from '../../../../shared/lib/transactions-controller-utils'; export default function ExchangeRateDisplay({ primaryTokenValue, diff --git a/ui/pages/swaps/index.js b/ui/pages/swaps/index.js index e9331eda3..cc7e9d0a6 100644 --- a/ui/pages/swaps/index.js +++ b/ui/pages/swaps/index.js @@ -87,10 +87,10 @@ import { EVENT } from '../../../shared/constants/metametrics'; import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction'; import ActionableMessage from '../../components/ui/actionable-message'; import { MetaMetricsContext } from '../../contexts/metametrics'; +import { getSwapsTokensReceivedFromTxMeta } from '../../../shared/lib/transactions-controller-utils'; import { fetchTokens, fetchTopAssets, - getSwapsTokensReceivedFromTxMeta, fetchAggregatorMetadata, stxErrorTypes, } from './swaps.util'; diff --git a/ui/pages/swaps/main-quote-summary/main-quote-summary.js b/ui/pages/swaps/main-quote-summary/main-quote-summary.js index d186cdfae..d7ff9646a 100644 --- a/ui/pages/swaps/main-quote-summary/main-quote-summary.js +++ b/ui/pages/swaps/main-quote-summary/main-quote-summary.js @@ -1,12 +1,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import BigNumber from 'bignumber.js'; -import { calcTokenAmount } from '../../../helpers/utils/token-util'; -import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util'; import Tooltip from '../../../components/ui/tooltip'; import UrlIcon from '../../../components/ui/url-icon'; import ExchangeRateDisplay from '../exchange-rate-display'; import { formatSwapsValueForDisplay } from '../swaps.util'; +import { + calcTokenAmount, + toPrecisionWithoutTrailingZeros, +} from '../../../../shared/lib/transactions-controller-utils'; function getFontSizesAndLineHeights(fontSizeScore) { if (fontSizeScore <= 9) { diff --git a/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js b/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js index 17b2458bd..1137178ec 100644 --- a/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js +++ b/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js @@ -52,7 +52,6 @@ import { EVENT } from '../../../../shared/constants/metametrics'; import { SMART_TRANSACTION_STATUSES } from '../../../../shared/constants/transaction'; import SwapsFooter from '../swaps-footer'; -import { calcTokenAmount } from '../../../helpers/utils/token-util'; import { showRemainingTimeInMinAndSec, getFeeForSmartTransaction, @@ -60,6 +59,7 @@ import { import { MetaMetricsContext } from '../../../contexts/metametrics'; import CreateNewSwap from '../create-new-swap'; import ViewOnBlockExplorer from '../view-on-block-explorer'; +import { calcTokenAmount } from '../../../../shared/lib/transactions-controller-utils'; import SuccessIcon from './success-icon'; import RevertedIcon from './reverted-icon'; import CanceledIcon from './canceled-icon'; diff --git a/ui/pages/swaps/swaps.util.js b/ui/pages/swaps/swaps.util.js index f2f7c5327..49abc6ee4 100644 --- a/ui/pages/swaps/swaps.util.js +++ b/ui/pages/swaps/swaps.util.js @@ -1,10 +1,8 @@ -import log from 'loglevel'; import BigNumber from 'bignumber.js'; import abi from 'human-standard-token-abi'; import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP, ALLOWED_CONTRACT_ADDRESSES, - SWAPS_WRAPPED_TOKENS_ADDRESSES, ETHEREUM, POLYGON, BSC, @@ -12,179 +10,40 @@ import { AVALANCHE, SWAPS_API_V2_BASE_URL, SWAPS_DEV_API_V2_BASE_URL, - GAS_API_BASE_URL, - GAS_DEV_API_BASE_URL, SWAPS_CLIENT_ID, + SWAPS_WRAPPED_TOKENS_ADDRESSES, } from '../../../shared/constants/swaps'; -import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction'; import { isSwapsDefaultTokenAddress, isSwapsDefaultTokenSymbol, } from '../../../shared/modules/swaps.utils'; import { CHAIN_IDS, CURRENCY_SYMBOLS } from '../../../shared/constants/network'; -import { SECOND } from '../../../shared/constants/time'; +import { getValueFromWeiHex } from '../../helpers/utils/conversions.util'; +import { formatCurrency } from '../../helpers/utils/confirm-tx.util'; +import fetchWithCache from '../../../shared/lib/fetch-with-cache'; + +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; import { - calcTokenValue, + calcGasTotal, calcTokenAmount, -} from '../../helpers/utils/token-util'; -import { - constructTxParams, + decimalToHex, toPrecisionWithoutTrailingZeros, -} from '../../helpers/utils/util'; +} from '../../../shared/lib/transactions-controller-utils'; import { - decimalToHex, - getValueFromWeiHex, -} from '../../helpers/utils/conversions.util'; - -import { subtractCurrencies } from '../../../shared/modules/conversion.utils'; -import { formatCurrency } from '../../helpers/utils/confirm-tx.util'; -import fetchWithCache from '../../helpers/utils/fetch-with-cache'; - -import { calcGasTotal } from '../send/send.utils'; -import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; - -const TOKEN_TRANSFER_LOG_TOPIC_HASH = - '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; + calcTokenValue, + constructTxParams, + getBaseApi, + QUOTE_VALIDATORS, + truthyString, + validateData, +} from '../../../shared/lib/swaps-utils'; +import { SECOND } from '../../../shared/constants/time'; const CACHE_REFRESH_FIVE_MINUTES = 300000; const USD_CURRENCY_CODE = 'usd'; const clientIdHeader = { 'X-Client-Id': SWAPS_CLIENT_ID }; -/** - * @param {string} type - Type of an API call, e.g. "tokens" - * @param {string} chainId - * @returns string - */ -const getBaseUrlForNewSwapsApi = (type, chainId) => { - const useDevApis = process.env.SWAPS_USE_DEV_APIS; - const v2ApiBaseUrl = useDevApis - ? SWAPS_DEV_API_V2_BASE_URL - : SWAPS_API_V2_BASE_URL; - const gasApiBaseUrl = useDevApis ? GAS_DEV_API_BASE_URL : GAS_API_BASE_URL; - const noNetworkSpecificTypes = ['refreshTime']; // These types don't need network info in the URL. - if (noNetworkSpecificTypes.includes(type)) { - return v2ApiBaseUrl; - } - const chainIdDecimal = chainId && parseInt(chainId, 16); - const gasApiTypes = ['gasPrices']; - if (gasApiTypes.includes(type)) { - return `${gasApiBaseUrl}/networks/${chainIdDecimal}`; // Gas calculations are in its own repo. - } - return `${v2ApiBaseUrl}/networks/${chainIdDecimal}`; -}; - -const TEST_CHAIN_IDS = [CHAIN_IDS.GOERLI, CHAIN_IDS.LOCALHOST]; - -export const getBaseApi = function (type, chainId = CHAIN_IDS.MAINNET) { - // eslint-disable-next-line no-param-reassign - chainId = TEST_CHAIN_IDS.includes(chainId) ? CHAIN_IDS.MAINNET : chainId; - const baseUrl = getBaseUrlForNewSwapsApi(type, chainId); - const chainIdDecimal = chainId && parseInt(chainId, 16); - if (!baseUrl) { - throw new Error(`Swaps API calls are disabled for chainId: ${chainId}`); - } - switch (type) { - case 'trade': - return `${baseUrl}/trades?`; - case 'tokens': - return `${baseUrl}/tokens`; - case 'token': - return `${baseUrl}/token`; - case 'topAssets': - return `${baseUrl}/topAssets`; - case 'aggregatorMetadata': - return `${baseUrl}/aggregatorMetadata`; - case 'gasPrices': - return `${baseUrl}/gasPrices`; - case 'network': - // Only use v2 for this endpoint. - return `${SWAPS_API_V2_BASE_URL}/networks/${chainIdDecimal}`; - default: - throw new Error('getBaseApi requires an api call type'); - } -}; - -const validHex = (string) => Boolean(string?.match(/^0x[a-f0-9]+$/u)); -const truthyString = (string) => Boolean(string?.length); -const truthyDigitString = (string) => - truthyString(string) && Boolean(string.match(/^\d+$/u)); - -const QUOTE_VALIDATORS = [ - { - property: 'trade', - type: 'object', - validator: (trade) => - trade && - validHex(trade.data) && - isValidHexAddress(trade.to, { allowNonPrefixed: false }) && - isValidHexAddress(trade.from, { allowNonPrefixed: false }) && - truthyString(trade.value), - }, - { - property: 'approvalNeeded', - type: 'object', - validator: (approvalTx) => - approvalTx === null || - (approvalTx && - validHex(approvalTx.data) && - isValidHexAddress(approvalTx.to, { allowNonPrefixed: false }) && - isValidHexAddress(approvalTx.from, { allowNonPrefixed: false })), - }, - { - property: 'sourceAmount', - type: 'string', - validator: truthyDigitString, - }, - { - property: 'destinationAmount', - type: 'string', - validator: truthyDigitString, - }, - { - property: 'sourceToken', - type: 'string', - validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }), - }, - { - property: 'destinationToken', - type: 'string', - validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }), - }, - { - property: 'aggregator', - type: 'string', - validator: truthyString, - }, - { - property: 'aggType', - type: 'string', - validator: truthyString, - }, - { - property: 'error', - type: 'object', - validator: (error) => error === null || typeof error === 'object', - }, - { - property: 'averageGas', - type: 'number', - }, - { - property: 'maxGas', - type: 'number', - }, - { - property: 'gasEstimate', - type: 'number|undefined', - validator: (gasEstimate) => gasEstimate === undefined || gasEstimate > 0, - }, - { - property: 'fee', - type: 'number', - }, -]; - const TOKEN_VALIDATORS = [ { property: 'address', @@ -244,25 +103,6 @@ const SWAP_GAS_PRICE_VALIDATOR = [ }, ]; -function validateData(validators, object, urlUsed, logError = true) { - return validators.every(({ property, type, validator }) => { - const types = type.split('|'); - - const valid = - types.some((_type) => typeof object[property] === _type) && - (!validator || validator(object[property])); - if (!valid && logError) { - log.error( - `response to GET ${urlUsed} invalid for property ${property}; value was:`, - object[property], - '| type was: ', - typeof object[property], - ); - } - return valid; - }); -} - export const shouldEnableDirectWrapping = ( chainId, sourceToken, @@ -725,105 +565,6 @@ export function quotesToRenderableData( }); } -export function getSwapsTokensReceivedFromTxMeta( - tokenSymbol, - txMeta, - tokenAddress, - accountAddress, - tokenDecimals, - approvalTxMeta, - chainId, -) { - const txReceipt = txMeta?.txReceipt; - const networkAndAccountSupports1559 = - txMeta?.txReceipt?.type === TRANSACTION_ENVELOPE_TYPES.FEE_MARKET; - if (isSwapsDefaultTokenSymbol(tokenSymbol, chainId)) { - if ( - !txReceipt || - !txMeta || - !txMeta.postTxBalance || - !txMeta.preTxBalance - ) { - return null; - } - - if (txMeta.swapMetaData && txMeta.preTxBalance === txMeta.postTxBalance) { - // If preTxBalance and postTxBalance are equal, postTxBalance hasn't been updated on time - // because of the RPC provider delay, so we return an estimated receiving amount instead. - return txMeta.swapMetaData.token_to_amount; - } - - let approvalTxGasCost = '0x0'; - if (approvalTxMeta && approvalTxMeta.txReceipt) { - approvalTxGasCost = calcGasTotal( - approvalTxMeta.txReceipt.gasUsed, - networkAndAccountSupports1559 - ? approvalTxMeta.txReceipt.effectiveGasPrice // Base fee + priority fee. - : approvalTxMeta.txParams.gasPrice, - ); - } - - const gasCost = calcGasTotal( - txReceipt.gasUsed, - networkAndAccountSupports1559 - ? txReceipt.effectiveGasPrice - : txMeta.txParams.gasPrice, - ); - const totalGasCost = new BigNumber(gasCost, 16) - .plus(approvalTxGasCost, 16) - .toString(16); - - const preTxBalanceLessGasCost = subtractCurrencies( - txMeta.preTxBalance, - totalGasCost, - { - aBase: 16, - bBase: 16, - toNumericBase: 'hex', - }, - ); - - const ethReceived = subtractCurrencies( - txMeta.postTxBalance, - preTxBalanceLessGasCost, - { - aBase: 16, - bBase: 16, - fromDenomination: 'WEI', - toDenomination: 'ETH', - toNumericBase: 'dec', - numberOfDecimals: 6, - }, - ); - return ethReceived; - } - const txReceiptLogs = txReceipt?.logs; - if (txReceiptLogs && txReceipt?.status !== '0x0') { - const tokenTransferLog = txReceiptLogs.find((txReceiptLog) => { - const isTokenTransfer = - txReceiptLog.topics && - txReceiptLog.topics[0] === TOKEN_TRANSFER_LOG_TOPIC_HASH; - const isTransferFromGivenToken = txReceiptLog.address === tokenAddress; - const isTransferFromGivenAddress = - txReceiptLog.topics && - txReceiptLog.topics[2] && - txReceiptLog.topics[2].match(accountAddress.slice(2)); - return ( - isTokenTransfer && - isTransferFromGivenToken && - isTransferFromGivenAddress - ); - }); - return tokenTransferLog - ? toPrecisionWithoutTrailingZeros( - calcTokenAmount(tokenTransferLog.data, tokenDecimals).toString(10), - 6, - ) - : ''; - } - return null; -} - export function formatSwapsValueForDisplay(destinationAmount) { let amountToDisplay = toPrecisionWithoutTrailingZeros(destinationAmount, 12); if (amountToDisplay.match(/e[+-]/u)) { diff --git a/ui/pages/swaps/swaps.util.test.js b/ui/pages/swaps/swaps.util.test.js index 1f6e3c08b..597aebe40 100644 --- a/ui/pages/swaps/swaps.util.test.js +++ b/ui/pages/swaps/swaps.util.test.js @@ -1,6 +1,7 @@ import nock from 'nock'; import { MOCKS } from '../../../test/jest'; import { CHAIN_IDS, CURRENCY_SYMBOLS } from '../../../shared/constants/network'; +import { getSwapsTokensReceivedFromTxMeta } from '../../../shared/lib/transactions-controller-utils'; import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP, SWAPS_CHAINID_DEFAULT_TOKEN_MAP, @@ -13,6 +14,10 @@ import { GOERLI, AVALANCHE, } from '../../../shared/constants/swaps'; +import { + fetchTradesInfo, + shouldEnableDirectWrapping, +} from '../../../shared/lib/swaps-utils'; import { TOKENS, EXPECTED_TOKENS_RESULT, @@ -21,7 +26,6 @@ import { TOP_ASSETS, } from './swaps-util-test-constants'; import { - fetchTradesInfo, fetchTokens, fetchAggregatorMetadata, fetchTopAssets, @@ -29,12 +33,10 @@ import { getNetworkNameByChainId, getSwapsLivenessForNetwork, countDecimals, - shouldEnableDirectWrapping, showRemainingTimeInMinAndSec, - getSwapsTokensReceivedFromTxMeta, } from './swaps.util'; -jest.mock('../../helpers/utils/storage-helpers.js', () => ({ +jest.mock('../../../shared/lib/storage-helpers', () => ({ getStorageItem: jest.fn(), setStorageItem: jest.fn(), })); diff --git a/ui/pages/swaps/view-quote/view-quote.js b/ui/pages/swaps/view-quote/view-quote.js index b2ac7eaab..6371e1ad1 100644 --- a/ui/pages/swaps/view-quote/view-quote.js +++ b/ui/pages/swaps/view-quote/view-quote.js @@ -66,8 +66,6 @@ import { } from '../../../selectors'; import { getNativeCurrency, getTokens } from '../../../ducks/metamask/metamask'; -import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util'; - import { safeRefetchQuotes, setCustomApproveTxData, @@ -84,19 +82,11 @@ import { AWAITING_SWAP_ROUTE, } from '../../../helpers/constants/routes'; import { - calcTokenAmount, - calcTokenValue, - getTokenValueParam, -} from '../../../helpers/utils/token-util'; -import { - decimalToHex, decGWEIToHexWEI, - hexWEIToDecGWEI, addHexes, decWEIToDecETH, } from '../../../helpers/utils/conversions.util'; import MainQuoteSummary from '../main-quote-summary'; -import { calcGasTotal } from '../../send/send.utils'; import { getCustomTxParamsData } from '../../confirm-approve/confirm-approve.util'; import ActionableMessage from '../../../components/ui/actionable-message/actionable-message'; import { @@ -114,6 +104,15 @@ import Box from '../../../components/ui/box'; import { EVENT } from '../../../../shared/constants/metametrics'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import { parseStandardTokenTransactionData } from '../../../../shared/modules/transaction.utils'; +import { getTokenValueParam } from '../../../../shared/lib/metamask-controller-utils'; +import { + calcGasTotal, + calcTokenAmount, + decimalToHex, + hexWEIToDecGWEI, + toPrecisionWithoutTrailingZeros, +} from '../../../../shared/lib/transactions-controller-utils'; +import { calcTokenValue } from '../../../../shared/lib/swaps-utils'; import ViewQuotePriceDifference from './view-quote-price-difference'; let intervalId; diff --git a/ui/pages/unlock-page/unlock-page.component.js b/ui/pages/unlock-page/unlock-page.component.js index 12b784f4f..690124793 100644 --- a/ui/pages/unlock-page/unlock-page.component.js +++ b/ui/pages/unlock-page/unlock-page.component.js @@ -5,13 +5,13 @@ import getCaretCoordinates from 'textarea-caret'; import Button from '../../components/ui/button'; import TextField from '../../components/ui/text-field'; import Mascot from '../../components/ui/mascot'; -import { SUPPORT_LINK } from '../../helpers/constants/common'; import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; import { EVENT, EVENT_NAMES, CONTEXT_PROPS, } from '../../../shared/constants/metametrics'; +import { SUPPORT_LINK } from '../../../shared/lib/ui-utils'; export default class UnlockPage extends Component { static contextTypes = { diff --git a/ui/selectors/confirm-transaction.js b/ui/selectors/confirm-transaction.js index 2094bb032..4be828d44 100644 --- a/ui/selectors/confirm-transaction.js +++ b/ui/selectors/confirm-transaction.js @@ -1,6 +1,5 @@ import { createSelector } from 'reselect'; import txHelper from '../helpers/utils/tx-helper'; -import { calcTokenAmount } from '../helpers/utils/token-util'; import { roundExponential, getValueFromWeiHex, @@ -26,6 +25,7 @@ import { getMinimumGasTotalInHexWei, } from '../../shared/modules/gas.utils'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; +import { calcTokenAmount } from '../../shared/lib/transactions-controller-utils'; import { getAveragePriceEstimateInHexWEI } from './custom-gas'; import { getCurrentChainId, deprecatedGetCurrentNetworkId } from './selectors'; import { checkNetworkAndAccountSupports1559 } from '.'; diff --git a/ui/selectors/custom-gas.js b/ui/selectors/custom-gas.js index 326df611d..3717365cb 100644 --- a/ui/selectors/custom-gas.js +++ b/ui/selectors/custom-gas.js @@ -6,7 +6,6 @@ import { import { formatCurrency } from '../helpers/utils/confirm-tx.util'; import { decEthToConvertedCurrency as ethTotalToConvertedCurrency } from '../helpers/utils/conversions.util'; import { formatETHFee } from '../helpers/utils/formatters'; -import { calcGasTotal } from '../pages/send/send.utils'; import { getGasLimit, getGasPrice } from '../ducks/send'; import { @@ -19,6 +18,7 @@ import { isEIP1559Network, } from '../ducks/metamask/metamask'; import { GAS_ESTIMATE_TYPES } from '../helpers/constants/common'; +import { calcGasTotal } from '../../shared/lib/transactions-controller-utils'; import { getCurrentCurrency, getIsMainnet, getShouldShowFiat } from '.'; const NUMBER_OF_DECIMALS_SM_BTNS = 5; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 3febc928b..f1ccc3c8f 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -42,10 +42,7 @@ import { getAccountByAddress, getURLHostName, } from '../helpers/utils/util'; -import { - getValueFromWeiHex, - hexToDecimal, -} from '../helpers/utils/conversions.util'; +import { getValueFromWeiHex } from '../helpers/utils/conversions.util'; import { TEMPLATED_CONFIRMATION_MESSAGE_TYPES } from '../pages/confirmation/templates'; import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens'; @@ -68,6 +65,7 @@ import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; ///: BEGIN:ONLY_INCLUDE_IN(flask) import { SNAPS_VIEW_ROUTE } from '../helpers/constants/routes'; ///: END:ONLY_INCLUDE_IN +import { hexToDecimal } from '../../shared/lib/metamask-controller-utils'; /** * One of the only remaining valid uses of selecting the network subkey of the diff --git a/ui/selectors/transactions.js b/ui/selectors/transactions.js index 0afd68a0a..879703550 100644 --- a/ui/selectors/transactions.js +++ b/ui/selectors/transactions.js @@ -3,7 +3,6 @@ import { PRIORITY_STATUS_HASH, PENDING_STATUS_HASH, } from '../helpers/constants/transactions'; -import { hexToDecimal } from '../helpers/utils/conversions.util'; import txHelper from '../helpers/utils/tx-helper'; import { TRANSACTION_STATUSES, @@ -11,6 +10,7 @@ import { SMART_TRANSACTION_STATUSES, } from '../../shared/constants/transaction'; import { transactionMatchesNetwork } from '../../shared/modules/transaction.utils'; +import { hexToDecimal } from '../../shared/lib/metamask-controller-utils'; import { getCurrentChainId, deprecatedGetCurrentNetworkId, diff --git a/ui/store/actions.js b/ui/store/actions.js index a5566fc5e..aa22390b6 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -2,12 +2,8 @@ import log from 'loglevel'; import { captureException } from '@sentry/browser'; import { capitalize, isEqual } from 'lodash'; import getBuyUrl from '../../app/scripts/lib/buy-url'; -import { - fetchLocale, - loadRelativeTimeFormatLocaleData, -} from '../helpers/utils/i18n-helper'; import { getMethodDataAsync } from '../helpers/utils/transactions.util'; -import switchDirection from '../helpers/utils/switch-direction'; +import switchDirection from '../../shared/lib/switch-direction'; import { ENVIRONMENT_TYPE_NOTIFICATION, ORIGIN_METAMASK, @@ -17,7 +13,6 @@ import { import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util'; import txHelper from '../helpers/utils/tx-helper'; import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util'; -import { decimalToHex } from '../helpers/utils/conversions.util'; import { getMetaMaskAccounts, getPermittedAccountsForCurrentTab, @@ -46,6 +41,11 @@ import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import { NOTIFICATIONS_EXPIRATION_DELAY } from '../helpers/constants/notifications'; ///: END:ONLY_INCLUDE_IN import { setNewCustomNetworkAdded } from '../ducks/app/app'; +import { decimalToHex } from '../../shared/lib/transactions-controller-utils'; +import { + fetchLocale, + loadRelativeTimeFormatLocaleData, +} from '../helpers/utils/i18n-helper'; import * as actionConstants from './actionConstants'; import { generateActionId,