1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

extract determineTransactionType from tx controller (#13737)

This commit is contained in:
Brad Decker 2022-03-07 12:54:36 -06:00 committed by GitHub
parent 4bb3ba4aef
commit 9cea401022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 262 additions and 274 deletions

View File

@ -3,8 +3,8 @@ import { warn } from 'loglevel';
import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi'; import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi';
import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts'; import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts';
import { MINUTE } from '../../../shared/constants/time'; import { MINUTE } from '../../../shared/constants/time';
import { isEqualCaseInsensitive } from '../../../ui/helpers/utils/util';
import { MAINNET_CHAIN_ID } from '../../../shared/constants/network'; import { MAINNET_CHAIN_ID } from '../../../shared/constants/network';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
// By default, poll every 3 minutes // By default, poll every 3 minutes
const DEFAULT_INTERVAL = MINUTE * 3; const DEFAULT_INTERVAL = MINUTE * 3;

View File

@ -28,7 +28,7 @@ import {
} from '../../../ui/pages/swaps/swaps.util'; } from '../../../ui/pages/swaps/swaps.util';
import fetchWithCache from '../../../ui/helpers/utils/fetch-with-cache'; import fetchWithCache from '../../../ui/helpers/utils/fetch-with-cache';
import { MINUTE, SECOND } from '../../../shared/constants/time'; 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'; 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 // The MAX_GAS_LIMIT is a number that is higher than the maximum gas costs we have observed on any aggregator

View File

@ -3,10 +3,8 @@ import { ObservableStore } from '@metamask/obs-store';
import { bufferToHex, keccak, toBuffer, isHexString } from 'ethereumjs-util'; import { bufferToHex, keccak, toBuffer, isHexString } from 'ethereumjs-util';
import EthQuery from 'ethjs-query'; import EthQuery from 'ethjs-query';
import { ethErrors } from 'eth-rpc-errors'; import { ethErrors } from 'eth-rpc-errors';
import abi from 'human-standard-token-abi';
import Common from '@ethereumjs/common'; import Common from '@ethereumjs/common';
import { TransactionFactory } from '@ethereumjs/tx'; import { TransactionFactory } from '@ethereumjs/tx';
import { ethers } from 'ethers';
import NonceTracker from 'nonce-tracker'; import NonceTracker from 'nonce-tracker';
import log from 'loglevel'; import log from 'loglevel';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
@ -47,16 +45,15 @@ import {
NETWORK_TYPE_RPC, NETWORK_TYPE_RPC,
CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP, CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP,
} from '../../../../shared/constants/network'; } from '../../../../shared/constants/network';
import { isEIP1559Transaction } from '../../../../shared/modules/transaction.utils'; import {
import { readAddressAsContract } from '../../../../shared/modules/contract-utils'; determineTransactionType,
import { isEqualCaseInsensitive } from '../../../../ui/helpers/utils/util'; isEIP1559Transaction,
} from '../../../../shared/modules/transaction.utils';
import TransactionStateManager from './tx-state-manager'; import TransactionStateManager from './tx-state-manager';
import TxGasUtil from './tx-gas-utils'; import TxGasUtil from './tx-gas-utils';
import PendingTransactionTracker from './pending-tx-tracker'; import PendingTransactionTracker from './pending-tx-tracker';
import * as txUtils from './lib/util'; 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 MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory
const SWAP_TRANSACTION_TYPES = [ const SWAP_TRANSACTION_TYPES = [
@ -641,7 +638,7 @@ export default class TransactionController extends EventEmitter {
* `generateTxMeta` adds the default txMeta properties to the passed object. * `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 * 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 * 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({ let txMeta = this.txStateManager.generateTxMeta({
txParams: normalizedTxParams, txParams: normalizedTxParams,
@ -669,8 +666,9 @@ export default class TransactionController extends EventEmitter {
} }
} }
const { type, getCodeResponse } = await this._determineTransactionType( const { type, getCodeResponse } = await determineTransactionType(
txParams, txParams,
this.query,
); );
txMeta.type = transactionType || type; 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 * Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions
* in the list have the same nonce * in the list have the same nonce

View File

@ -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 () { describe('#getPendingTransactions', function () {
it('should show only submitted and approved transactions as pending transaction', function () { it('should show only submitted and approved transactions as pending transaction', function () {
txController.txStateManager._addTransactionsToState([ txController.txStateManager._addTransactionsToState([

View File

@ -82,7 +82,7 @@ import {
import { hexToDecimal } from '../../ui/helpers/utils/conversions.util'; import { hexToDecimal } from '../../ui/helpers/utils/conversions.util';
import { getTokenValueParam } from '../../ui/helpers/utils/token-util'; import { getTokenValueParam } from '../../ui/helpers/utils/token-util';
import { getTransactionData } from '../../ui/helpers/utils/transactions.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 ComposableObservableStore from './lib/ComposableObservableStore';
import AccountTracker from './lib/account-tracker'; import AccountTracker from './lib/account-tracker';
import createLoggerMiddleware from './lib/createLoggerMiddleware'; import createLoggerMiddleware from './lib/createLoggerMiddleware';

View File

@ -0,0 +1,6 @@
export function isEqualCaseInsensitive(value1, value2) {
if (typeof value1 !== 'string' || typeof value2 !== 'string') {
return false;
}
return value1.toLowerCase() === value2.toLowerCase();
}

View File

@ -1,4 +1,24 @@
import { isHexString } from 'ethereumjs-util'; 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) { export function transactionMatchesNetwork(transaction, chainId, networkId) {
if (typeof transaction.chainId !== 'undefined') { if (typeof transaction.chainId !== 'undefined') {
@ -61,3 +81,53 @@ export function txParamsAreDappSuggested(transaction) {
transaction?.dappSuggestedGasFees?.maxFeePerGas === maxFeePerGas) 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 };
}

View File

@ -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('Transaction.utils', function () {
describe('isEIP1559Transaction', function () { describe('isEIP1559Transaction', function () {
@ -80,4 +87,143 @@ describe('Transaction.utils', function () {
).toBe(false); ).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',
});
});
});
}); });

View File

@ -18,11 +18,7 @@ import {
BLOCK_SIZES, BLOCK_SIZES,
} from '../../../helpers/constants/design-system'; } from '../../../helpers/constants/design-system';
import { useI18nContext } from '../../../hooks/useI18nContext'; import { useI18nContext } from '../../../hooks/useI18nContext';
import { import { getAssetImageURL, shortenAddress } from '../../../helpers/utils/util';
getAssetImageURL,
isEqualCaseInsensitive,
shortenAddress,
} from '../../../helpers/utils/util';
import { import {
getCurrentChainId, getCurrentChainId,
getIpfsGateway, getIpfsGateway,
@ -54,6 +50,7 @@ import InfoTooltip from '../../ui/info-tooltip';
import { ERC721 } from '../../../helpers/constants/common'; import { ERC721 } from '../../../helpers/constants/common';
import { usePrevious } from '../../../hooks/usePrevious'; import { usePrevious } from '../../../hooks/usePrevious';
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
export default function CollectibleDetails({ collectible }) { export default function CollectibleDetails({ collectible }) {
const { const {

View File

@ -13,7 +13,7 @@ import Button from '../../ui/button';
import { TOKEN_CATEGORY_HASH } from '../../../helpers/constants/transactions'; import { TOKEN_CATEGORY_HASH } from '../../../helpers/constants/transactions';
import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP } from '../../../../shared/constants/swaps'; import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP } from '../../../../shared/constants/swaps';
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
import { isEqualCaseInsensitive } from '../../../helpers/utils/util'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
const PAGE_INCREMENT = 10; const PAGE_INCREMENT = 10;

View File

@ -20,7 +20,7 @@ import {
import { conversionUtil } from '../../../shared/modules/conversion.utils'; import { conversionUtil } from '../../../shared/modules/conversion.utils';
import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas'; import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas';
import { isEqualCaseInsensitive } from '../../helpers/utils/util'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
// Actions // Actions
const createActionType = (action) => `metamask/confirm-transaction/${action}`; const createActionType = (action) => `metamask/confirm-transaction/${action}`;

View File

@ -14,9 +14,9 @@ import {
import { updateTransaction } from '../../store/actions'; import { updateTransaction } from '../../store/actions';
import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck'; import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck';
import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util';
import { isEqualCaseInsensitive } from '../../helpers/utils/util';
import { KEYRING_TYPES } from '../../../shared/constants/hardware-wallets'; import { KEYRING_TYPES } from '../../../shared/constants/hardware-wallets';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
export default function reduceMetamask(state = {}, action) { export default function reduceMetamask(state = {}, action) {
const metamaskState = { const metamaskState = {

View File

@ -78,7 +78,6 @@ import {
isDefaultMetaMaskChain, isDefaultMetaMaskChain,
isOriginContractAddress, isOriginContractAddress,
isValidDomainName, isValidDomainName,
isEqualCaseInsensitive,
} from '../../helpers/utils/util'; } from '../../helpers/utils/util';
import { import {
getGasEstimateType, getGasEstimateType,
@ -102,6 +101,7 @@ import {
import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction'; import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction';
import { readAddressAsContract } from '../../../shared/modules/contract-utils'; import { readAddressAsContract } from '../../../shared/modules/contract-utils';
import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys'; import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
// typedefs // typedefs
/** /**
* @typedef {import('@reduxjs/toolkit').PayloadAction} PayloadAction * @typedef {import('@reduxjs/toolkit').PayloadAction} PayloadAction

View File

@ -65,14 +65,6 @@ export function isDefaultMetaMaskChain(chainId) {
return false; 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) { export function valuesFor(obj) {
if (!obj) { if (!obj) {
return []; return [];

View File

@ -3,11 +3,11 @@ import { useRouteMatch } from 'react-router-dom';
import { getTokens } from '../ducks/metamask/metamask'; import { getTokens } from '../ducks/metamask/metamask';
import { getCurrentChainId } from '../selectors'; import { getCurrentChainId } from '../selectors';
import { ASSET_ROUTE } from '../helpers/constants/routes'; import { ASSET_ROUTE } from '../helpers/constants/routes';
import { isEqualCaseInsensitive } from '../helpers/utils/util';
import { import {
SWAPS_CHAINID_DEFAULT_TOKEN_MAP, SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
ETH_SWAPS_TOKEN_OBJECT, ETH_SWAPS_TOKEN_OBJECT,
} from '../../shared/constants/swaps'; } from '../../shared/constants/swaps';
import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
/** /**
* Returns a token object for the asset that is currently being viewed. * Returns a token object for the asset that is currently being viewed.

View File

@ -7,7 +7,7 @@ import {
} from '../selectors'; } from '../selectors';
import { getTokenFiatAmount } from '../helpers/utils/token-util'; import { getTokenFiatAmount } from '../helpers/utils/token-util';
import { getConversionRate } from '../ducks/metamask/metamask'; 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 * Get the token balance converted to fiat and formatted for display

View File

@ -3,7 +3,7 @@ import TokenTracker from '@metamask/eth-token-tracker';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import { getCurrentChainId, getSelectedAddress } from '../selectors'; import { getCurrentChainId, getSelectedAddress } from '../selectors';
import { SECOND } from '../../shared/constants/time'; import { SECOND } from '../../shared/constants/time';
import { isEqualCaseInsensitive } from '../helpers/utils/util'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
import { useEqualityCheck } from './useEqualityCheck'; import { useEqualityCheck } from './useEqualityCheck';
export function useTokenTracker( export function useTokenTracker(

View File

@ -11,7 +11,6 @@ import {
getTokenValueParam, getTokenValueParam,
} from '../helpers/utils/token-util'; } from '../helpers/utils/token-util';
import { import {
isEqualCaseInsensitive,
formatDateWithYearContext, formatDateWithYearContext,
shortenAddress, shortenAddress,
stripHttpSchemes, stripHttpSchemes,
@ -28,6 +27,7 @@ import {
TRANSACTION_STATUSES, TRANSACTION_STATUSES,
} from '../../shared/constants/transaction'; } from '../../shared/constants/transaction';
import { captureSingleException } from '../store/actions'; import { captureSingleException } from '../store/actions';
import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
import { useI18nContext } from './useI18nContext'; import { useI18nContext } from './useI18nContext';
import { useTokenFiatAmount } from './useTokenFiatAmount'; import { useTokenFiatAmount } from './useTokenFiatAmount';
import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'; import { useUserPreferencedCurrency } from './useUserPreferencedCurrency';

View File

@ -1,10 +1,10 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Redirect, useParams } from 'react-router-dom'; import { Redirect, useParams } from 'react-router-dom';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import CollectibleDetails from '../../components/app/collectible-details/collectible-details'; import CollectibleDetails from '../../components/app/collectible-details/collectible-details';
import { getCollectibles, getTokens } from '../../ducks/metamask/metamask'; import { getCollectibles, getTokens } from '../../ducks/metamask/metamask';
import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
import { isEqualCaseInsensitive } from '../../helpers/utils/util';
import NativeAsset from './components/native-asset'; import NativeAsset from './components/native-asset';
import TokenAsset from './components/token-asset'; import TokenAsset from './components/token-asset';

View File

@ -7,7 +7,7 @@ import TokenBalance from '../../components/ui/token-balance';
import { I18nContext } from '../../contexts/i18n'; import { I18nContext } from '../../contexts/i18n';
import { MetaMetricsContext } from '../../contexts/metametrics'; import { MetaMetricsContext } from '../../contexts/metametrics';
import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; 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) { function getTokenName(name, symbol) {
return typeof name === 'undefined' ? symbol : `${name} (${symbol})`; return typeof name === 'undefined' ? symbol : `${name} (${symbol})`;

View File

@ -43,7 +43,7 @@ import AdvancedGasFeePopover from '../../components/app/advanced-gas-fee-popover
import EditGasFeePopover from '../../components/app/edit-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 EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component';
import Loading from '../../components/ui/loading-screen'; 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 { getCustomTxParamsData } from './confirm-approve.util';
import ConfirmApproveContent from './confirm-approve-content'; import ConfirmApproveContent from './confirm-approve-content';

View File

@ -13,7 +13,7 @@ import {
getTokenValueParam, getTokenValueParam,
} from '../../helpers/utils/token-util'; } from '../../helpers/utils/token-util';
import { hexWEIToDecETH } from '../../helpers/utils/conversions.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'; import ConfirmTokenTransactionBase from './confirm-token-transaction-base.component';
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {

View File

@ -14,11 +14,7 @@ import {
setDefaultHomeActiveTabName, setDefaultHomeActiveTabName,
} from '../../store/actions'; } from '../../store/actions';
import { isBalanceSufficient, calcGasTotal } from '../send/send.utils'; import { isBalanceSufficient, calcGasTotal } from '../send/send.utils';
import { import { shortenAddress, valuesFor } from '../../helpers/utils/util';
isEqualCaseInsensitive,
shortenAddress,
valuesFor,
} from '../../helpers/utils/util';
import { import {
getAdvancedInlineGasShown, getAdvancedInlineGasShown,
getCustomNonceValue, getCustomNonceValue,
@ -55,6 +51,7 @@ import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app'; import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app';
import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas'; import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import ConfirmTransactionBase from './confirm-transaction-base.component'; import ConfirmTransactionBase from './confirm-transaction-base.component';
let customNonceValue = ''; let customNonceValue = '';

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import Fuse from 'fuse.js'; import Fuse from 'fuse.js';
import InputAdornment from '@material-ui/core/InputAdornment'; import InputAdornment from '@material-ui/core/InputAdornment';
import TextField from '../../../components/ui/text-field'; 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 { export default class TokenSearch extends Component {
static contextTypes = { static contextTypes = {

View File

@ -7,7 +7,7 @@ import TokenListDisplay from '../../../../components/app/token-list-display';
import UserPreferencedCurrencyDisplay from '../../../../components/app/user-preferenced-currency-display'; import UserPreferencedCurrencyDisplay from '../../../../components/app/user-preferenced-currency-display';
import { ERC20, ERC721, PRIMARY } from '../../../../helpers/constants/common'; import { ERC20, ERC721, PRIMARY } from '../../../../helpers/constants/common';
import { ASSET_TYPES } from '../../../../ducks/send'; 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 { export default class SendAssetRow extends Component {
static propTypes = { static propTypes = {

View File

@ -3,9 +3,9 @@ import PropTypes from 'prop-types';
import Fuse from 'fuse.js'; import Fuse from 'fuse.js';
import InputAdornment from '@material-ui/core/InputAdornment'; import InputAdornment from '@material-ui/core/InputAdornment';
import TextField from '../../../components/ui/text-field'; import TextField from '../../../components/ui/text-field';
import { isEqualCaseInsensitive } from '../../../helpers/utils/util';
import { I18nContext } from '../../../contexts/i18n'; import { I18nContext } from '../../../contexts/i18n';
import SearchIcon from '../../../components/ui/search-icon'; import SearchIcon from '../../../components/ui/search-icon';
import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
export default function SettingsSearch({ export default function SettingsSearch({
onSearch, onSearch,

View File

@ -77,10 +77,7 @@ import {
hexToDecimal, hexToDecimal,
} from '../../../helpers/utils/conversions.util'; } from '../../../helpers/utils/conversions.util';
import { calcTokenAmount } from '../../../helpers/utils/token-util'; import { calcTokenAmount } from '../../../helpers/utils/token-util';
import { import { getURLHostName } from '../../../helpers/utils/util';
getURLHostName,
isEqualCaseInsensitive,
} from '../../../helpers/utils/util';
import { usePrevious } from '../../../hooks/usePrevious'; import { usePrevious } from '../../../hooks/usePrevious';
import { useTokenTracker } from '../../../hooks/useTokenTracker'; import { useTokenTracker } from '../../../hooks/useTokenTracker';
import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount'; import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
@ -110,6 +107,7 @@ import {
shouldEnableDirectWrapping, shouldEnableDirectWrapping,
} from '../swaps.util'; } from '../swaps.util';
import SwapsFooter from '../swaps-footer'; import SwapsFooter from '../swaps-footer';
import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
const fuseSearchKeys = [ const fuseSearchKeys = [
{ name: 'name', weight: 0.499 }, { name: 'name', weight: 0.499 },

View File

@ -59,10 +59,7 @@ import {
} from '../../../selectors'; } from '../../../selectors';
import { getNativeCurrency, getTokens } from '../../../ducks/metamask/metamask'; import { getNativeCurrency, getTokens } from '../../../ducks/metamask/metamask';
import { import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util';
toPrecisionWithoutTrailingZeros,
isEqualCaseInsensitive,
} from '../../../helpers/utils/util';
import { import {
safeRefetchQuotes, safeRefetchQuotes,
@ -115,6 +112,7 @@ import CountdownTimer from '../countdown-timer';
import SwapsFooter from '../swaps-footer'; import SwapsFooter from '../swaps-footer';
import PulseLoader from '../../../components/ui/pulse-loader'; // TODO: Replace this with a different loading component. import PulseLoader from '../../../components/ui/pulse-loader'; // TODO: Replace this with a different loading component.
import Box from '../../../components/ui/box'; import Box from '../../../components/ui/box';
import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
import ViewQuotePriceDifference from './view-quote-price-difference'; import ViewQuotePriceDifference from './view-quote-price-difference';
let intervalId; let intervalId;

View File

@ -4,7 +4,6 @@ import { Redirect, useHistory, useParams } from 'react-router-dom';
import { getTokens } from '../../ducks/metamask/metamask'; import { getTokens } from '../../ducks/metamask/metamask';
import { getUseTokenDetection, getTokenList } from '../../selectors'; import { getUseTokenDetection, getTokenList } from '../../selectors';
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard'; import { useCopyToClipboard } from '../../hooks/useCopyToClipboard';
import { isEqualCaseInsensitive } from '../../helpers/utils/util';
import Identicon from '../../components/ui/identicon'; import Identicon from '../../components/ui/identicon';
import { I18nContext } from '../../contexts/i18n'; import { I18nContext } from '../../contexts/i18n';
import { useTokenTracker } from '../../hooks/useTokenTracker'; import { useTokenTracker } from '../../hooks/useTokenTracker';
@ -25,6 +24,7 @@ import {
TEXT_ALIGN, TEXT_ALIGN,
OVERFLOW_WRAP, OVERFLOW_WRAP,
} from '../../helpers/constants/design-system'; } from '../../helpers/constants/design-system';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
export default function TokenDetailsPage() { export default function TokenDetailsPage() {
const dispatch = useDispatch(); const dispatch = useDispatch();

View File

@ -3,7 +3,7 @@ import configureMockStore from 'redux-mock-store';
import { fireEvent } from '@testing-library/react'; import { fireEvent } from '@testing-library/react';
import { renderWithProvider } from '../../../test/lib/render-helpers'; import { renderWithProvider } from '../../../test/lib/render-helpers';
import Identicon from '../../components/ui/identicon/identicon.component'; 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'; import TokenDetailsPage from './token-details-page';
const testTokenAddress = '0xaD6D458402F60fD3Bd25163575031ACDce07538A'; const testTokenAddress = '0xaD6D458402F60fD3Bd25163575031ACDce07538A';

View File

@ -25,7 +25,7 @@ import {
getMaximumGasTotalInHexWei, getMaximumGasTotalInHexWei,
getMinimumGasTotalInHexWei, getMinimumGasTotalInHexWei,
} from '../../shared/modules/gas.utils'; } from '../../shared/modules/gas.utils';
import { isEqualCaseInsensitive } from '../helpers/utils/util'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
import { getAveragePriceEstimateInHexWEI } from './custom-gas'; import { getAveragePriceEstimateInHexWEI } from './custom-gas';
import { getCurrentChainId, deprecatedGetCurrentNetworkId } from './selectors'; import { getCurrentChainId, deprecatedGetCurrentNetworkId } from './selectors';
import { checkNetworkAndAccountSupports1559 } from '.'; import { checkNetworkAndAccountSupports1559 } from '.';

View File

@ -33,11 +33,7 @@ import {
ALLOWED_SWAPS_CHAIN_IDS, ALLOWED_SWAPS_CHAIN_IDS,
} from '../../shared/constants/swaps'; } from '../../shared/constants/swaps';
import { import { shortenAddress, getAccountByAddress } from '../helpers/utils/util';
shortenAddress,
getAccountByAddress,
isEqualCaseInsensitive,
} from '../helpers/utils/util';
import { import {
getValueFromWeiHex, getValueFromWeiHex,
hexToDecimal, hexToDecimal,
@ -60,6 +56,7 @@ import {
getLedgerWebHidConnectedStatus, getLedgerWebHidConnectedStatus,
getLedgerTransportStatus, getLedgerTransportStatus,
} from '../ducks/app/app'; } 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 * One of the only remaining valid uses of selecting the network subkey of the

View File

@ -9,7 +9,6 @@ import {
} from '../helpers/utils/i18n-helper'; } from '../helpers/utils/i18n-helper';
import { getMethodDataAsync } from '../helpers/utils/transactions.util'; import { getMethodDataAsync } from '../helpers/utils/transactions.util';
import { getSymbolAndDecimals } from '../helpers/utils/token-util'; import { getSymbolAndDecimals } from '../helpers/utils/token-util';
import { isEqualCaseInsensitive } from '../helpers/utils/util';
import switchDirection from '../helpers/utils/switch-direction'; import switchDirection from '../helpers/utils/switch-direction';
import { import {
ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_NOTIFICATION,
@ -35,6 +34,7 @@ import {
LEDGER_USB_VENDOR_ID, LEDGER_USB_VENDOR_ID,
} from '../../shared/constants/hardware-wallets'; } from '../../shared/constants/hardware-wallets';
import { parseSmartTransactionsError } from '../pages/swaps/swaps.util'; import { parseSmartTransactionsError } from '../pages/swaps/swaps.util';
import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
import * as actionConstants from './actionConstants'; import * as actionConstants from './actionConstants';
let background = null; let background = null;