From 9cea401022befee81650cab6a6b72291addf52be Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Mon, 7 Mar 2022 12:54:36 -0600 Subject: [PATCH] extract determineTransactionType from tx controller (#13737) --- app/scripts/controllers/detect-tokens.js | 2 +- app/scripts/controllers/swaps.js | 2 +- app/scripts/controllers/transactions/index.js | 77 +-------- .../controllers/transactions/index.test.js | 150 ------------------ app/scripts/metamask-controller.js | 2 +- shared/modules/string-utils.js | 6 + shared/modules/transaction.utils.js | 70 ++++++++ shared/modules/transaction.utils.test.js | 148 ++++++++++++++++- .../collectible-details.js | 7 +- .../transaction-list.component.js | 2 +- .../confirm-transaction.duck.js | 2 +- ui/ducks/metamask/metamask.js | 2 +- ui/ducks/send/send.js | 2 +- ui/helpers/utils/util.js | 8 - ui/hooks/useCurrentAsset.js | 2 +- ui/hooks/useTokenFiatAmount.js | 2 +- ui/hooks/useTokenTracker.js | 2 +- ui/hooks/useTransactionDisplayData.js | 2 +- ui/pages/asset/asset.js | 2 +- .../confirm-add-suggested-token.component.js | 2 +- ui/pages/confirm-approve/confirm-approve.js | 2 +- ...onfirm-token-transaction-base.container.js | 2 +- .../confirm-transaction-base.container.js | 7 +- .../token-search/token-search.component.js | 2 +- .../send-asset-row.component.js | 2 +- .../settings-search/settings-search.js | 2 +- ui/pages/swaps/build-quote/build-quote.js | 6 +- ui/pages/swaps/view-quote/view-quote.js | 6 +- ui/pages/token-details/token-details-page.js | 2 +- .../token-details/token-details-page.test.js | 2 +- ui/selectors/confirm-transaction.js | 2 +- ui/selectors/selectors.js | 7 +- ui/store/actions.js | 2 +- 33 files changed, 262 insertions(+), 274 deletions(-) create mode 100644 shared/modules/string-utils.js diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index c2f740e7e..c98c1683c 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -3,8 +3,8 @@ import { warn } from 'loglevel'; import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi'; import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts'; import { MINUTE } from '../../../shared/constants/time'; -import { isEqualCaseInsensitive } from '../../../ui/helpers/utils/util'; import { MAINNET_CHAIN_ID } from '../../../shared/constants/network'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; // By default, poll every 3 minutes const DEFAULT_INTERVAL = MINUTE * 3; diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index 8b16a86dc..6eb800793 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -28,7 +28,7 @@ import { } from '../../../ui/pages/swaps/swaps.util'; import fetchWithCache from '../../../ui/helpers/utils/fetch-with-cache'; import { MINUTE, SECOND } from '../../../shared/constants/time'; -import { isEqualCaseInsensitive } from '../../../ui/helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-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 93947c1e2..f68b88e65 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -3,10 +3,8 @@ import { ObservableStore } from '@metamask/obs-store'; import { bufferToHex, keccak, toBuffer, isHexString } from 'ethereumjs-util'; import EthQuery from 'ethjs-query'; import { ethErrors } from 'eth-rpc-errors'; -import abi from 'human-standard-token-abi'; import Common from '@ethereumjs/common'; import { TransactionFactory } from '@ethereumjs/tx'; -import { ethers } from 'ethers'; import NonceTracker from 'nonce-tracker'; import log from 'loglevel'; import BigNumber from 'bignumber.js'; @@ -47,16 +45,15 @@ import { NETWORK_TYPE_RPC, CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP, } from '../../../../shared/constants/network'; -import { isEIP1559Transaction } from '../../../../shared/modules/transaction.utils'; -import { readAddressAsContract } from '../../../../shared/modules/contract-utils'; -import { isEqualCaseInsensitive } from '../../../../ui/helpers/utils/util'; +import { + determineTransactionType, + isEIP1559Transaction, +} from '../../../../shared/modules/transaction.utils'; import TransactionStateManager from './tx-state-manager'; import TxGasUtil from './tx-gas-utils'; import PendingTransactionTracker from './pending-tx-tracker'; import * as txUtils from './lib/util'; -const hstInterface = new ethers.utils.Interface(abi); - const MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory const SWAP_TRANSACTION_TYPES = [ @@ -641,7 +638,7 @@ export default class TransactionController extends EventEmitter { * `generateTxMeta` adds the default txMeta properties to the passed object. * These include the tx's `id`. As we use the id for determining order of * txes in the tx-state-manager, it is necessary to call the asynchronous - * method `this._determineTransactionType` after `generateTxMeta`. + * method `determineTransactionType` after `generateTxMeta`. */ let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams, @@ -669,8 +666,9 @@ export default class TransactionController extends EventEmitter { } } - const { type, getCodeResponse } = await this._determineTransactionType( + const { type, getCodeResponse } = await determineTransactionType( txParams, + this.query, ); txMeta.type = transactionType || type; @@ -1726,67 +1724,6 @@ export default class TransactionController extends EventEmitter { }); } - /** - * @typedef { 'transfer' | 'approve' | 'transferfrom' | 'contractInteraction'| 'simpleSend' } InferrableTransactionTypes - */ - - /** - * @typedef {Object} InferTransactionTypeResult - * @property {InferrableTransactionTypes} type - The type of transaction - * @property {string} getCodeResponse - The contract code, in hex format if - * it exists. '0x0' or '0x' are also indicators of non-existent contract - * code - */ - - /** - * Determines the type of the transaction by analyzing the txParams. - * This method will return one of the types defined in shared/constants/transactions - * It will never return TRANSACTION_TYPE_CANCEL or TRANSACTION_TYPE_RETRY as these - * represent specific events that we control from the extension and are added manually - * at transaction creation. - * - * @param {Object} txParams - Parameters for the transaction - * @returns {InferTransactionTypeResult} - */ - async _determineTransactionType(txParams) { - const { data, to } = txParams; - let name; - try { - name = data && hstInterface.parseTransaction({ data }).name; - } catch (error) { - log.debug('Failed to parse transaction data.', error, data); - } - - const tokenMethodName = [ - TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, - TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, - TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, - ].find((methodName) => isEqualCaseInsensitive(methodName, name)); - - let result; - if (data && tokenMethodName) { - result = tokenMethodName; - } else if (data && !to) { - result = TRANSACTION_TYPES.DEPLOY_CONTRACT; - } - - let contractCode; - - if (!result) { - const { - contractCode: resultCode, - isContractAddress, - } = await readAddressAsContract(this.query, to); - - contractCode = resultCode; - result = isContractAddress - ? TRANSACTION_TYPES.CONTRACT_INTERACTION - : TRANSACTION_TYPES.SIMPLE_SEND; - } - - return { type: result, getCodeResponse: contractCode }; - } - /** * Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions * in the list have the same nonce diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index 37501acfb..0bbed0998 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -1305,156 +1305,6 @@ describe('Transaction Controller', function () { }); }); - describe('#_determineTransactionType', function () { - it('should return a simple send type when to is truthy but data is falsy', async function () { - const result = await txController._determineTransactionType({ - to: '0xabc', - data: '', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.SIMPLE_SEND, - getCodeResponse: null, - }); - }); - - it('should return a token transfer type when data is for the respective method call', async function () { - const result = await txController._determineTransactionType({ - to: '0xabc', - data: - '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, - getCodeResponse: undefined, - }); - }); - - it('should return a token approve type when data is for the respective method call', async function () { - const result = await txController._determineTransactionType({ - to: '0xabc', - data: - '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, - getCodeResponse: undefined, - }); - }); - - it('should return a contract deployment type when to is falsy and there is data', async function () { - const result = await txController._determineTransactionType({ - to: '', - data: '0xabd', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.DEPLOY_CONTRACT, - getCodeResponse: undefined, - }); - }); - - it('should return a simple send type with a 0x getCodeResponse when there is data and but the to address is not a contract address', async function () { - const result = await txController._determineTransactionType({ - to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9', - data: '0xabd', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.SIMPLE_SEND, - getCodeResponse: '0x', - }); - }); - - it('should return a simple send type with a null getCodeResponse when to is truthy and there is data and but getCode returns an error', async function () { - const result = await txController._determineTransactionType({ - to: '0xabc', - data: '0xabd', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.SIMPLE_SEND, - getCodeResponse: null, - }); - }); - - it('should return a contract interaction type with the correct getCodeResponse when to is truthy and there is data and it is not a token transaction', async function () { - const _providerResultStub = { - // 1 gwei - eth_gasPrice: '0x0de0b6b3a7640000', - // by default, all accounts are external accounts (not contracts) - eth_getCode: '0xa', - }; - const _provider = createTestProviderTools({ - scaffold: _providerResultStub, - }).provider; - const _fromAccount = getTestAccounts()[0]; - const _blockTrackerStub = new EventEmitter(); - _blockTrackerStub.getCurrentBlock = noop; - _blockTrackerStub.getLatestBlock = noop; - const _txController = new TransactionController({ - provider: _provider, - getGasPrice() { - return '0xee6b2800'; - }, - networkStore: new ObservableStore(currentNetworkId), - getCurrentChainId: () => currentChainId, - txHistoryLimit: 10, - blockTracker: _blockTrackerStub, - signTransaction: (ethTx) => - new Promise((resolve) => { - ethTx.sign(_fromAccount.key); - resolve(); - }), - getParticipateInMetrics: () => false, - }); - const result = await _txController._determineTransactionType({ - to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9', - data: 'abd', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.CONTRACT_INTERACTION, - getCodeResponse: '0x0a', - }); - }); - - it('should return a contract interaction type with the correct getCodeResponse when to is a contract address and data is falsy', async function () { - const _providerResultStub = { - // 1 gwei - eth_gasPrice: '0x0de0b6b3a7640000', - // by default, all accounts are external accounts (not contracts) - eth_getCode: '0xa', - }; - const _provider = createTestProviderTools({ - scaffold: _providerResultStub, - }).provider; - const _fromAccount = getTestAccounts()[0]; - const _blockTrackerStub = new EventEmitter(); - _blockTrackerStub.getCurrentBlock = noop; - _blockTrackerStub.getLatestBlock = noop; - const _txController = new TransactionController({ - provider: _provider, - getGasPrice() { - return '0xee6b2800'; - }, - networkStore: new ObservableStore(currentNetworkId), - getCurrentChainId: () => currentChainId, - txHistoryLimit: 10, - blockTracker: _blockTrackerStub, - signTransaction: (ethTx) => - new Promise((resolve) => { - ethTx.sign(_fromAccount.key); - resolve(); - }), - getParticipateInMetrics: () => false, - }); - const result = await _txController._determineTransactionType({ - to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9', - data: '', - }); - assert.deepEqual(result, { - type: TRANSACTION_TYPES.CONTRACT_INTERACTION, - getCodeResponse: '0x0a', - }); - }); - }); - describe('#getPendingTransactions', function () { it('should show only submitted and approved transactions as pending transaction', function () { txController.txStateManager._addTransactionsToState([ diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index bf85bac18..cec719181 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -82,7 +82,7 @@ import { import { hexToDecimal } from '../../ui/helpers/utils/conversions.util'; import { getTokenValueParam } from '../../ui/helpers/utils/token-util'; import { getTransactionData } from '../../ui/helpers/utils/transactions.util'; -import { isEqualCaseInsensitive } from '../../ui/helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import ComposableObservableStore from './lib/ComposableObservableStore'; import AccountTracker from './lib/account-tracker'; import createLoggerMiddleware from './lib/createLoggerMiddleware'; diff --git a/shared/modules/string-utils.js b/shared/modules/string-utils.js new file mode 100644 index 000000000..fc496227f --- /dev/null +++ b/shared/modules/string-utils.js @@ -0,0 +1,6 @@ +export function isEqualCaseInsensitive(value1, value2) { + if (typeof value1 !== 'string' || typeof value2 !== 'string') { + return false; + } + return value1.toLowerCase() === value2.toLowerCase(); +} diff --git a/shared/modules/transaction.utils.js b/shared/modules/transaction.utils.js index bd0b6fdf4..8995b6c02 100644 --- a/shared/modules/transaction.utils.js +++ b/shared/modules/transaction.utils.js @@ -1,4 +1,24 @@ import { isHexString } from 'ethereumjs-util'; +import { ethers } from 'ethers'; +import abi from 'human-standard-token-abi'; +import log from 'loglevel'; +import { TRANSACTION_TYPES } from '../constants/transaction'; +import { readAddressAsContract } from './contract-utils'; +import { isEqualCaseInsensitive } from './string-utils'; + +/** + * @typedef { 'transfer' | 'approve' | 'transferfrom' | 'contractInteraction'| 'simpleSend' } InferrableTransactionTypes + */ + +/** + * @typedef {Object} InferTransactionTypeResult + * @property {InferrableTransactionTypes} type - The type of transaction + * @property {string} getCodeResponse - The contract code, in hex format if + * it exists. '0x0' or '0x' are also indicators of non-existent contract + * code + */ + +const hstInterface = new ethers.utils.Interface(abi); export function transactionMatchesNetwork(transaction, chainId, networkId) { if (typeof transaction.chainId !== 'undefined') { @@ -61,3 +81,53 @@ export function txParamsAreDappSuggested(transaction) { transaction?.dappSuggestedGasFees?.maxFeePerGas === maxFeePerGas) ); } + +/** + * Determines the type of the transaction by analyzing the txParams. + * This method will return one of the types defined in shared/constants/transactions + * It will never return TRANSACTION_TYPE_CANCEL or TRANSACTION_TYPE_RETRY as these + * represent specific events that we control from the extension and are added manually + * at transaction creation. + * + * @param {Object} txParams - Parameters for the transaction + * @param {EthQuery} query - EthQuery instance + * @returns {InferTransactionTypeResult} + */ +export async function determineTransactionType(txParams, query) { + const { data, to } = txParams; + let name; + try { + name = data && hstInterface.parseTransaction({ data }).name; + } catch (error) { + log.debug('Failed to parse transaction data.', error, data); + } + + const tokenMethodName = [ + TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, + TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, + TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + ].find((methodName) => isEqualCaseInsensitive(methodName, name)); + + let result; + if (data && tokenMethodName) { + result = tokenMethodName; + } else if (data && !to) { + result = TRANSACTION_TYPES.DEPLOY_CONTRACT; + } + + let contractCode; + + if (!result) { + const { + contractCode: resultCode, + isContractAddress, + } = await readAddressAsContract(query, to); + + contractCode = resultCode; + result = isContractAddress + ? TRANSACTION_TYPES.CONTRACT_INTERACTION + : TRANSACTION_TYPES.SIMPLE_SEND; + } + + return { type: result, getCodeResponse: contractCode }; +} diff --git a/shared/modules/transaction.utils.test.js b/shared/modules/transaction.utils.test.js index 3a333caa7..f6d816160 100644 --- a/shared/modules/transaction.utils.test.js +++ b/shared/modules/transaction.utils.test.js @@ -1,4 +1,11 @@ -import { isEIP1559Transaction, isLegacyTransaction } from './transaction.utils'; +import EthQuery from 'ethjs-query'; +import { createTestProviderTools } from '../../test/stub/provider'; +import { TRANSACTION_TYPES } from '../constants/transaction'; +import { + determineTransactionType, + isEIP1559Transaction, + isLegacyTransaction, +} from './transaction.utils'; describe('Transaction.utils', function () { describe('isEIP1559Transaction', function () { @@ -80,4 +87,143 @@ describe('Transaction.utils', function () { ).toBe(false); }); }); + + describe('determineTransactionType', function () { + const genericProvider = createTestProviderTools().provider; + const query = new EthQuery(genericProvider); + + it('should return a simple send type when to is truthy but data is falsy', async function () { + const result = await determineTransactionType( + { + to: '0xabc', + data: '', + }, + query, + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.SIMPLE_SEND, + getCodeResponse: null, + }); + }); + + it('should return a token transfer type when data is for the respective method call', async function () { + const result = await determineTransactionType( + { + to: '0xabc', + data: + '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a', + }, + query, + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, + getCodeResponse: undefined, + }); + }); + + it('should return a token approve type when data is for the respective method call', async function () { + const result = await determineTransactionType( + { + to: '0xabc', + data: + '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005', + }, + query, + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, + getCodeResponse: undefined, + }); + }); + + it('should return a contract deployment type when to is falsy and there is data', async function () { + const result = await determineTransactionType( + { + to: '', + data: '0xabd', + }, + query, + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.DEPLOY_CONTRACT, + getCodeResponse: undefined, + }); + }); + + it('should return a simple send type with a 0x getCodeResponse when there is data and but the to address is not a contract address', async function () { + const result = await determineTransactionType( + { + to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9', + data: '0xabd', + }, + query, + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.SIMPLE_SEND, + getCodeResponse: '0x', + }); + }); + + it('should return a simple send type with a null getCodeResponse when to is truthy and there is data and but getCode returns an error', async function () { + const result = await determineTransactionType( + { + to: '0xabc', + data: '0xabd', + }, + query, + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.SIMPLE_SEND, + getCodeResponse: null, + }); + }); + + it('should return a contract interaction type with the correct getCodeResponse when to is truthy and there is data and it is not a token transaction', async function () { + const _providerResultStub = { + // 1 gwei + eth_gasPrice: '0x0de0b6b3a7640000', + // by default, all accounts are external accounts (not contracts) + eth_getCode: '0xa', + }; + const _provider = createTestProviderTools({ + scaffold: _providerResultStub, + }).provider; + + const result = await determineTransactionType( + { + to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9', + data: 'abd', + }, + new EthQuery(_provider), + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.CONTRACT_INTERACTION, + getCodeResponse: '0x0a', + }); + }); + + it('should return a contract interaction type with the correct getCodeResponse when to is a contract address and data is falsy', async function () { + const _providerResultStub = { + // 1 gwei + eth_gasPrice: '0x0de0b6b3a7640000', + // by default, all accounts are external accounts (not contracts) + eth_getCode: '0xa', + }; + const _provider = createTestProviderTools({ + scaffold: _providerResultStub, + }).provider; + + const result = await determineTransactionType( + { + to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9', + data: '', + }, + new EthQuery(_provider), + ); + expect(result).toMatchObject({ + type: TRANSACTION_TYPES.CONTRACT_INTERACTION, + getCodeResponse: '0x0a', + }); + }); + }); }); diff --git a/ui/components/app/collectible-details/collectible-details.js b/ui/components/app/collectible-details/collectible-details.js index 195953545..9217a61e4 100644 --- a/ui/components/app/collectible-details/collectible-details.js +++ b/ui/components/app/collectible-details/collectible-details.js @@ -18,11 +18,7 @@ import { BLOCK_SIZES, } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { - getAssetImageURL, - isEqualCaseInsensitive, - shortenAddress, -} from '../../../helpers/utils/util'; +import { getAssetImageURL, shortenAddress } from '../../../helpers/utils/util'; import { getCurrentChainId, getIpfsGateway, @@ -54,6 +50,7 @@ import InfoTooltip from '../../ui/info-tooltip'; import { ERC721 } from '../../../helpers/constants/common'; import { usePrevious } from '../../../hooks/usePrevious'; import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; export default function CollectibleDetails({ collectible }) { const { diff --git a/ui/components/app/transaction-list/transaction-list.component.js b/ui/components/app/transaction-list/transaction-list.component.js index 8a21da314..b8ced7a8b 100644 --- a/ui/components/app/transaction-list/transaction-list.component.js +++ b/ui/components/app/transaction-list/transaction-list.component.js @@ -13,7 +13,7 @@ import Button from '../../ui/button'; import { TOKEN_CATEGORY_HASH } from '../../../helpers/constants/transactions'; import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP } from '../../../../shared/constants/swaps'; import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; -import { isEqualCaseInsensitive } from '../../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; const PAGE_INCREMENT = 10; diff --git a/ui/ducks/confirm-transaction/confirm-transaction.duck.js b/ui/ducks/confirm-transaction/confirm-transaction.duck.js index 3652a7d0a..c05050f28 100644 --- a/ui/ducks/confirm-transaction/confirm-transaction.duck.js +++ b/ui/ducks/confirm-transaction/confirm-transaction.duck.js @@ -20,7 +20,7 @@ import { import { conversionUtil } from '../../../shared/modules/conversion.utils'; import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; // Actions const createActionType = (action) => `metamask/confirm-transaction/${action}`; diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 7aa2e1220..dcf4b033f 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -14,9 +14,9 @@ import { import { updateTransaction } from '../../store/actions'; import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck'; import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; import { KEYRING_TYPES } from '../../../shared/constants/hardware-wallets'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; export default function reduceMetamask(state = {}, action) { const metamaskState = { diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index abd74a4dc..d01e954fc 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -78,7 +78,6 @@ import { isDefaultMetaMaskChain, isOriginContractAddress, isValidDomainName, - isEqualCaseInsensitive, } from '../../helpers/utils/util'; import { getGasEstimateType, @@ -102,6 +101,7 @@ import { import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; // typedefs /** * @typedef {import('@reduxjs/toolkit').PayloadAction} PayloadAction diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index 9cb8a6ed3..fb4f92ce3 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -65,14 +65,6 @@ export function isDefaultMetaMaskChain(chainId) { return false; } -// Both inputs should be strings. This method is currently used to compare tokenAddress hex strings. -export function isEqualCaseInsensitive(value1, value2) { - if (typeof value1 !== 'string' || typeof value2 !== 'string') { - return false; - } - return value1.toLowerCase() === value2.toLowerCase(); -} - export function valuesFor(obj) { if (!obj) { return []; diff --git a/ui/hooks/useCurrentAsset.js b/ui/hooks/useCurrentAsset.js index f98fcab82..97fa27fc1 100644 --- a/ui/hooks/useCurrentAsset.js +++ b/ui/hooks/useCurrentAsset.js @@ -3,11 +3,11 @@ import { useRouteMatch } from 'react-router-dom'; import { getTokens } from '../ducks/metamask/metamask'; import { getCurrentChainId } from '../selectors'; import { ASSET_ROUTE } from '../helpers/constants/routes'; -import { isEqualCaseInsensitive } from '../helpers/utils/util'; import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP, ETH_SWAPS_TOKEN_OBJECT, } from '../../shared/constants/swaps'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; /** * Returns a token object for the asset that is currently being viewed. diff --git a/ui/hooks/useTokenFiatAmount.js b/ui/hooks/useTokenFiatAmount.js index 9fe9c816f..4565bc4f6 100644 --- a/ui/hooks/useTokenFiatAmount.js +++ b/ui/hooks/useTokenFiatAmount.js @@ -7,7 +7,7 @@ import { } from '../selectors'; import { getTokenFiatAmount } from '../helpers/utils/token-util'; import { getConversionRate } from '../ducks/metamask/metamask'; -import { isEqualCaseInsensitive } from '../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; /** * Get the token balance converted to fiat and formatted for display diff --git a/ui/hooks/useTokenTracker.js b/ui/hooks/useTokenTracker.js index 29772a1de..b58562168 100644 --- a/ui/hooks/useTokenTracker.js +++ b/ui/hooks/useTokenTracker.js @@ -3,7 +3,7 @@ import TokenTracker from '@metamask/eth-token-tracker'; import { shallowEqual, useSelector } from 'react-redux'; import { getCurrentChainId, getSelectedAddress } from '../selectors'; import { SECOND } from '../../shared/constants/time'; -import { isEqualCaseInsensitive } from '../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import { useEqualityCheck } from './useEqualityCheck'; export function useTokenTracker( diff --git a/ui/hooks/useTransactionDisplayData.js b/ui/hooks/useTransactionDisplayData.js index e12c75b9f..a56181e03 100644 --- a/ui/hooks/useTransactionDisplayData.js +++ b/ui/hooks/useTransactionDisplayData.js @@ -11,7 +11,6 @@ import { getTokenValueParam, } from '../helpers/utils/token-util'; import { - isEqualCaseInsensitive, formatDateWithYearContext, shortenAddress, stripHttpSchemes, @@ -28,6 +27,7 @@ import { TRANSACTION_STATUSES, } from '../../shared/constants/transaction'; import { captureSingleException } from '../store/actions'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import { useI18nContext } from './useI18nContext'; import { useTokenFiatAmount } from './useTokenFiatAmount'; import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'; diff --git a/ui/pages/asset/asset.js b/ui/pages/asset/asset.js index 42c710fd7..e88298243 100644 --- a/ui/pages/asset/asset.js +++ b/ui/pages/asset/asset.js @@ -1,10 +1,10 @@ import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { Redirect, useParams } from 'react-router-dom'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import CollectibleDetails from '../../components/app/collectible-details/collectible-details'; import { getCollectibles, getTokens } from '../../ducks/metamask/metamask'; import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; import NativeAsset from './components/native-asset'; import TokenAsset from './components/token-asset'; diff --git a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js index 45a5e8566..8e7d30ec0 100644 --- a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js +++ b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -7,7 +7,7 @@ import TokenBalance from '../../components/ui/token-balance'; import { I18nContext } from '../../contexts/i18n'; import { MetaMetricsContext } from '../../contexts/metametrics'; import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; function getTokenName(name, symbol) { return typeof name === 'undefined' ? symbol : `${name} (${symbol})`; diff --git a/ui/pages/confirm-approve/confirm-approve.js b/ui/pages/confirm-approve/confirm-approve.js index 5ba090fed..1a2663367 100644 --- a/ui/pages/confirm-approve/confirm-approve.js +++ b/ui/pages/confirm-approve/confirm-approve.js @@ -43,7 +43,7 @@ import AdvancedGasFeePopover from '../../components/app/advanced-gas-fee-popover import EditGasFeePopover from '../../components/app/edit-gas-fee-popover'; import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component'; import Loading from '../../components/ui/loading-screen'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import { getCustomTxParamsData } from './confirm-approve.util'; import ConfirmApproveContent from './confirm-approve-content'; diff --git a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js index dbbc5cbca..5ab40cd65 100644 --- a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js +++ b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js @@ -13,7 +13,7 @@ import { getTokenValueParam, } from '../../helpers/utils/token-util'; import { hexWEIToDecETH } from '../../helpers/utils/conversions.util'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import ConfirmTokenTransactionBase from './confirm-token-transaction-base.component'; const mapStateToProps = (state, ownProps) => { 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 c6a6598ad..6a2313c97 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -14,11 +14,7 @@ import { setDefaultHomeActiveTabName, } from '../../store/actions'; import { isBalanceSufficient, calcGasTotal } from '../send/send.utils'; -import { - isEqualCaseInsensitive, - shortenAddress, - valuesFor, -} from '../../helpers/utils/util'; +import { shortenAddress, valuesFor } from '../../helpers/utils/util'; import { getAdvancedInlineGasShown, getCustomNonceValue, @@ -55,6 +51,7 @@ import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import ConfirmTransactionBase from './confirm-transaction-base.component'; let customNonceValue = ''; diff --git a/ui/pages/import-token/token-search/token-search.component.js b/ui/pages/import-token/token-search/token-search.component.js index 6a6a2c0dc..84085b46b 100644 --- a/ui/pages/import-token/token-search/token-search.component.js +++ b/ui/pages/import-token/token-search/token-search.component.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import Fuse from 'fuse.js'; import InputAdornment from '@material-ui/core/InputAdornment'; import TextField from '../../../components/ui/text-field'; -import { isEqualCaseInsensitive } from '../../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; export default class TokenSearch extends Component { static contextTypes = { diff --git a/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js b/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js index 3f28f967c..34b77eff2 100644 --- a/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js +++ b/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js @@ -7,7 +7,7 @@ import TokenListDisplay from '../../../../components/app/token-list-display'; import UserPreferencedCurrencyDisplay from '../../../../components/app/user-preferenced-currency-display'; import { ERC20, ERC721, PRIMARY } from '../../../../helpers/constants/common'; import { ASSET_TYPES } from '../../../../ducks/send'; -import { isEqualCaseInsensitive } from '../../../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; export default class SendAssetRow extends Component { static propTypes = { diff --git a/ui/pages/settings/settings-search/settings-search.js b/ui/pages/settings/settings-search/settings-search.js index e580d39ca..57b206684 100644 --- a/ui/pages/settings/settings-search/settings-search.js +++ b/ui/pages/settings/settings-search/settings-search.js @@ -3,9 +3,9 @@ import PropTypes from 'prop-types'; import Fuse from 'fuse.js'; import InputAdornment from '@material-ui/core/InputAdornment'; import TextField from '../../../components/ui/text-field'; -import { isEqualCaseInsensitive } from '../../../helpers/utils/util'; import { I18nContext } from '../../../contexts/i18n'; import SearchIcon from '../../../components/ui/search-icon'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; export default function SettingsSearch({ onSearch, diff --git a/ui/pages/swaps/build-quote/build-quote.js b/ui/pages/swaps/build-quote/build-quote.js index 3cb8dbe1d..b3526b5dd 100644 --- a/ui/pages/swaps/build-quote/build-quote.js +++ b/ui/pages/swaps/build-quote/build-quote.js @@ -77,10 +77,7 @@ import { hexToDecimal, } from '../../../helpers/utils/conversions.util'; import { calcTokenAmount } from '../../../helpers/utils/token-util'; -import { - getURLHostName, - isEqualCaseInsensitive, -} from '../../../helpers/utils/util'; +import { getURLHostName } from '../../../helpers/utils/util'; import { usePrevious } from '../../../hooks/usePrevious'; import { useTokenTracker } from '../../../hooks/useTokenTracker'; import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount'; @@ -110,6 +107,7 @@ import { shouldEnableDirectWrapping, } from '../swaps.util'; import SwapsFooter from '../swaps-footer'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; const fuseSearchKeys = [ { name: 'name', weight: 0.499 }, diff --git a/ui/pages/swaps/view-quote/view-quote.js b/ui/pages/swaps/view-quote/view-quote.js index 622985580..316c7842c 100644 --- a/ui/pages/swaps/view-quote/view-quote.js +++ b/ui/pages/swaps/view-quote/view-quote.js @@ -59,10 +59,7 @@ import { } from '../../../selectors'; import { getNativeCurrency, getTokens } from '../../../ducks/metamask/metamask'; -import { - toPrecisionWithoutTrailingZeros, - isEqualCaseInsensitive, -} from '../../../helpers/utils/util'; +import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util'; import { safeRefetchQuotes, @@ -115,6 +112,7 @@ import CountdownTimer from '../countdown-timer'; import SwapsFooter from '../swaps-footer'; import PulseLoader from '../../../components/ui/pulse-loader'; // TODO: Replace this with a different loading component. import Box from '../../../components/ui/box'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import ViewQuotePriceDifference from './view-quote-price-difference'; let intervalId; diff --git a/ui/pages/token-details/token-details-page.js b/ui/pages/token-details/token-details-page.js index 7441e1994..e7e7fb763 100644 --- a/ui/pages/token-details/token-details-page.js +++ b/ui/pages/token-details/token-details-page.js @@ -4,7 +4,6 @@ import { Redirect, useHistory, useParams } from 'react-router-dom'; import { getTokens } from '../../ducks/metamask/metamask'; import { getUseTokenDetection, getTokenList } from '../../selectors'; import { useCopyToClipboard } from '../../hooks/useCopyToClipboard'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; import Identicon from '../../components/ui/identicon'; import { I18nContext } from '../../contexts/i18n'; import { useTokenTracker } from '../../hooks/useTokenTracker'; @@ -25,6 +24,7 @@ import { TEXT_ALIGN, OVERFLOW_WRAP, } from '../../helpers/constants/design-system'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; export default function TokenDetailsPage() { const dispatch = useDispatch(); diff --git a/ui/pages/token-details/token-details-page.test.js b/ui/pages/token-details/token-details-page.test.js index e5385098f..a4eb5bc74 100644 --- a/ui/pages/token-details/token-details-page.test.js +++ b/ui/pages/token-details/token-details-page.test.js @@ -3,7 +3,7 @@ import configureMockStore from 'redux-mock-store'; import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../test/lib/render-helpers'; import Identicon from '../../components/ui/identicon/identicon.component'; -import { isEqualCaseInsensitive } from '../../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import TokenDetailsPage from './token-details-page'; const testTokenAddress = '0xaD6D458402F60fD3Bd25163575031ACDce07538A'; diff --git a/ui/selectors/confirm-transaction.js b/ui/selectors/confirm-transaction.js index 2f1d9d193..e3e8f6efb 100644 --- a/ui/selectors/confirm-transaction.js +++ b/ui/selectors/confirm-transaction.js @@ -25,7 +25,7 @@ import { getMaximumGasTotalInHexWei, getMinimumGasTotalInHexWei, } from '../../shared/modules/gas.utils'; -import { isEqualCaseInsensitive } from '../helpers/utils/util'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import { getAveragePriceEstimateInHexWEI } from './custom-gas'; import { getCurrentChainId, deprecatedGetCurrentNetworkId } from './selectors'; import { checkNetworkAndAccountSupports1559 } from '.'; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index a74b7facf..0b90528ca 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -33,11 +33,7 @@ import { ALLOWED_SWAPS_CHAIN_IDS, } from '../../shared/constants/swaps'; -import { - shortenAddress, - getAccountByAddress, - isEqualCaseInsensitive, -} from '../helpers/utils/util'; +import { shortenAddress, getAccountByAddress } from '../helpers/utils/util'; import { getValueFromWeiHex, hexToDecimal, @@ -60,6 +56,7 @@ import { getLedgerWebHidConnectedStatus, getLedgerTransportStatus, } from '../ducks/app/app'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; /** * One of the only remaining valid uses of selecting the network subkey of the diff --git a/ui/store/actions.js b/ui/store/actions.js index 97e17de8a..84643cc34 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -9,7 +9,6 @@ import { } from '../helpers/utils/i18n-helper'; import { getMethodDataAsync } from '../helpers/utils/transactions.util'; import { getSymbolAndDecimals } from '../helpers/utils/token-util'; -import { isEqualCaseInsensitive } from '../helpers/utils/util'; import switchDirection from '../helpers/utils/switch-direction'; import { ENVIRONMENT_TYPE_NOTIFICATION, @@ -35,6 +34,7 @@ import { LEDGER_USB_VENDOR_ID, } from '../../shared/constants/hardware-wallets'; import { parseSmartTransactionsError } from '../pages/swaps/swaps.util'; +import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import * as actionConstants from './actionConstants'; let background = null;