From b45ab7014489da9518f54205e333e0ba143d0596 Mon Sep 17 00:00:00 2001 From: Nodonisko Date: Thu, 3 Jul 2025 21:31:53 +0200 Subject: [PATCH 1/7] chore: faster address checksum --- .../AggregatedPercentageCrossChains.test.tsx | 5 ++- .../AggregatedPercentageCrossChains.tsx | 8 +++-- .../UI/AccountFromToInfoCard/AddressFrom.tsx | 4 +-- .../UI/PaymentRequest/AssetList/index.tsx | 4 +-- app/components/UI/TransactionElement/utils.js | 10 +++--- .../Settings/Contacts/ContactForm/index.js | 4 +-- .../confirmations/hooks/useAccountInfo.ts | 5 ++- .../Views/confirmations/legacy/Send/index.js | 10 +++--- .../legacy/SendFlow/SendTo/index.js | 4 +-- .../ApproveTransactionHeader.tsx | 5 ++- .../AddNickname/index.tsx | 4 +-- .../hooks/useAccounts/useAccounts.test.ts | 7 ++--- app/components/hooks/useExistingAddress.ts | 4 +-- app/selectors/earnController/earn/index.ts | 11 ++++--- app/util/address/index.ts | 27 +++++++++------- app/util/checkAddress.ts | 8 ++--- app/util/transactions/index.js | 31 +++++++++---------- 17 files changed, 81 insertions(+), 70 deletions(-) diff --git a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx index 9cdeb6a1d5fe..48d685bbd75c 100644 --- a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx +++ b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx @@ -23,10 +23,13 @@ jest.mock('../../../../selectors/currencyRateController', () => ({ })); jest.mock('ethereumjs-util', () => ({ - toChecksumAddress: jest.fn((address) => address), zeroAddress: jest.fn(() => '0x0000000000000000000000000000000000000000'), })); +jest.mock('@metamask/utils', () => ({ + getChecksumAddress: jest.fn((address) => address), +})); + describe('AggregatedPercentageCrossChains', () => { const mockStore = configureStore([]); let store: Store; diff --git a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx index 202ba3663903..588ef361c842 100644 --- a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx +++ b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx @@ -10,7 +10,8 @@ import { FORMATTED_VALUE_PRICE_TEST_ID, FORMATTED_PERCENTAGE_TEST_ID, } from './AggregatedPercentage.constants'; -import { toChecksumAddress, zeroAddress } from 'ethereumjs-util'; +import { zeroAddress } from 'ethereumjs-util'; +import { getChecksumAddress, Hex } from '@metamask/utils'; import { selectTokenMarketData } from '../../../../selectors/tokenRatesController'; import { MarketDataMapping, @@ -18,7 +19,6 @@ import { } from '../../../../components/hooks/useGetFormattedTokensPerChain'; import { getFormattedAmountChange, getPercentageTextColor } from './utils'; import { AggregatedPercentageCrossChainsProps } from './AggregatedPercentageCrossChains.types'; - export const getCalculatedTokenAmount1dAgo = ( tokenFiatBalance: number, tokenPricePercentChange1dAgo: number, @@ -48,7 +48,9 @@ const AggregatedPercentageCrossChains = ({ const totalPerChain1dAgoERC20 = tokensWithBalances.reduce( (total1dAgo: number, item: { address: string }, idx: number) => { const found = - crossChainMarketData?.[chainId]?.[toChecksumAddress(item.address)]; + crossChainMarketData?.[chainId]?.[ + getChecksumAddress(item.address as Hex) + ]; const tokenFiat1dAgo = getCalculatedTokenAmount1dAgo( tokenFiatBalances[idx], diff --git a/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx b/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx index 1010dbfcb127..45dc3f614c81 100644 --- a/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx +++ b/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx @@ -1,4 +1,4 @@ -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress, Hex } from '@metamask/utils'; import React, { useEffect, useState } from 'react'; import { View } from 'react-native'; import { useSelector } from 'react-redux'; @@ -62,7 +62,7 @@ const AddressFrom = ({ const accountsByChainId = useSelector(selectAccountsByChainId); const internalAccounts = useSelector(selectInternalEvmAccounts); - const activeAddress = toChecksumAddress(from); + const activeAddress = getChecksumAddress(from as Hex); const networkName = useSelector(selectEvmNetworkName); const networkImage = useSelector(selectEvmNetworkImageSource); diff --git a/app/components/UI/PaymentRequest/AssetList/index.tsx b/app/components/UI/PaymentRequest/AssetList/index.tsx index c9482ff1192a..8921c3abe196 100644 --- a/app/components/UI/PaymentRequest/AssetList/index.tsx +++ b/app/components/UI/PaymentRequest/AssetList/index.tsx @@ -6,7 +6,7 @@ import { fontStyles } from '../../../../styles/common'; import Identicon from '../../Identicon'; import NetworkMainAssetLogo from '../../NetworkMainAssetLogo'; import { useSelector } from 'react-redux'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress } from '@metamask/utils'; import { useTheme } from '../../../../util/theme'; import { selectTokenList } from '../../../../selectors/tokenListController'; import { ImportTokenViewSelectorsIDs } from '../../../../../e2e/selectors/wallet/ImportTokenView.selectors'; @@ -103,7 +103,7 @@ const AssetList = ({ return ; } const token = - tokenList?.[toChecksumAddress(address)] || + tokenList?.[getChecksumAddress(address)] || tokenList?.[address.toLowerCase()]; const iconUrl = token?.iconUrl; if (!iconUrl) { diff --git a/app/components/UI/TransactionElement/utils.js b/app/components/UI/TransactionElement/utils.js index b9dfc0c2270e..2962dd8541c6 100644 --- a/app/components/UI/TransactionElement/utils.js +++ b/app/components/UI/TransactionElement/utils.js @@ -24,7 +24,7 @@ import { getActionKey, TRANSACTION_TYPES, } from '../../../util/transactions'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress } from '@metamask/utils'; import { swapsUtils } from '@metamask/swaps-controller'; import { isSwapsNativeAsset } from '../Swaps/utils'; import Engine from '../../../core/Engine'; @@ -242,7 +242,7 @@ export function decodeIncomingTransfer(args) { : undefined; const exchangeRate = token && contractExchangeRates - ? contractExchangeRates[toChecksumAddress(token.address)]?.price + ? contractExchangeRates[getChecksumAddress(token.address)]?.price : undefined; let renderTokenFiatAmount, renderTokenFiatNumber; @@ -402,7 +402,8 @@ function decodeTransferFromTx(args) { const renderFrom = renderFullAddress(addressFrom); const renderTo = renderFullAddress(addressTo); - const ticker = networkConfigurationsByChainId?.[txChainId]?.nativeCurrency || args.ticker; + const ticker = + networkConfigurationsByChainId?.[txChainId]?.nativeCurrency || args.ticker; const { SENT_COLLECTIBLE, RECEIVED_COLLECTIBLE } = TRANSACTION_TYPES; const transactionType = @@ -473,7 +474,8 @@ function decodeDeploymentTx(args) { actionKey, primaryCurrency, } = args; - const ticker = networkConfigurationsByChainId?.[txChainId]?.nativeCurrency || args.ticker; + const ticker = + networkConfigurationsByChainId?.[txChainId]?.nativeCurrency || args.ticker; const totalGas = calculateTotalGas(txParams); const renderTotalEth = `${renderFromWei(totalGas)} ${ticker}`; diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.js b/app/components/Views/Settings/Contacts/ContactForm/index.js index 1239c04f3d35..d5d945d2bb29 100644 --- a/app/components/Views/Settings/Contacts/ContactForm/index.js +++ b/app/components/Views/Settings/Contacts/ContactForm/index.js @@ -13,7 +13,7 @@ import PropTypes from 'prop-types'; import { getEditableOptions } from '../../../../UI/Navbar'; import StyledButton from '../../../../UI/StyledButton'; import Engine from '../../../../../core/Engine'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress } from '@metamask/utils'; import { connect } from 'react-redux'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { strings } from '../../../../../../locales/i18n'; @@ -340,7 +340,7 @@ class ContactForm extends PureComponent { } AddressBookController.set( - toChecksumAddress(toEnsAddress || address), + getChecksumAddress(toEnsAddress || address), name, contactChainId || chainId, memo, diff --git a/app/components/Views/confirmations/hooks/useAccountInfo.ts b/app/components/Views/confirmations/hooks/useAccountInfo.ts index dcd9a9686da2..fd50a3dd4ab9 100644 --- a/app/components/Views/confirmations/hooks/useAccountInfo.ts +++ b/app/components/Views/confirmations/hooks/useAccountInfo.ts @@ -1,7 +1,6 @@ -import { toChecksumAddress } from 'ethereumjs-util'; import { useMemo } from 'react'; import { useSelector } from 'react-redux'; -import { Hex } from '@metamask/utils'; +import { Hex, getChecksumAddress } from '@metamask/utils'; import Engine from '../../../../core/Engine'; import useAddressBalance from '../../../../components/hooks/useAddressBalance/useAddressBalance'; @@ -13,7 +12,7 @@ import I18n from '../../../../../locales/i18n'; const useAccountInfo = (address: string, chainId: Hex) => { const internalAccounts = useSelector(selectInternalAccounts); - const activeAddress = toChecksumAddress(address); + const activeAddress = getChecksumAddress(address as Hex); const { addressBalance: accountBalance } = useAddressBalance( undefined, address, diff --git a/app/components/Views/confirmations/legacy/Send/index.js b/app/components/Views/confirmations/legacy/Send/index.js index 8a83319ea7ba..47f6d63e756b 100644 --- a/app/components/Views/confirmations/legacy/Send/index.js +++ b/app/components/Views/confirmations/legacy/Send/index.js @@ -17,7 +17,7 @@ import { fromWei, fromTokenMinimalUnit, } from '../../../../../util/number'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress } from '@metamask/utils'; import { strings } from '../../../../../../locales/i18n'; import { getTransactionOptionsTitle } from '../../../../UI/Navbar'; import { connect } from 'react-redux'; @@ -443,7 +443,7 @@ class Send extends PureComponent { */ handleTokenDeeplink = async (address) => { const { tokens, tokenList } = this.props; - address = toChecksumAddress(address); + address = getChecksumAddress(address); // First check if we have token information in token list if (address in tokenList) { return tokenList[address]; @@ -582,7 +582,7 @@ class Send extends PureComponent { let checksummedAddress = null; if (assetType === 'ETH') { - checksummedAddress = toChecksumAddress(transactionMeta.transaction.to); + checksummedAddress = getChecksumAddress(transactionMeta.transaction.to); } else if (assetType === 'ERC20') { try { const [addressTo] = decodeTransferData( @@ -590,7 +590,7 @@ class Send extends PureComponent { transactionMeta.transaction.data, ); if (addressTo) { - checksummedAddress = toChecksumAddress(addressTo); + checksummedAddress = getChecksumAddress(addressTo); } } catch (e) { Logger.log('Error decoding transfer data', transactionMeta.data); @@ -603,7 +603,7 @@ class Send extends PureComponent { ); const addressTo = data[1]; if (addressTo) { - checksummedAddress = toChecksumAddress(addressTo); + checksummedAddress = getChecksumAddress(addressTo); } } catch (e) { Logger.log('Error decoding transfer data', transactionMeta.data); diff --git a/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js b/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js index d8ea02a2030a..ba8ab2aac3d3 100644 --- a/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js +++ b/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js @@ -2,7 +2,7 @@ import React, { Fragment, PureComponent } from 'react'; import { View, ScrollView, Alert, Platform, BackHandler } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress } from '@metamask/utils'; import { SafeAreaView } from 'react-native-safe-area-context'; import Icon from 'react-native-vector-icons/FontAwesome'; import AddressList from '../AddressList'; @@ -490,7 +490,7 @@ class SendFlow extends PureComponent { safeChecksumAddress = (address) => { try { - return toChecksumAddress(address); + return getChecksumAddress(address); } catch (error) { return address; } diff --git a/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx b/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx index 998ab03d45a3..dfa8409d653c 100644 --- a/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx +++ b/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx @@ -1,8 +1,7 @@ -import { toChecksumAddress } from 'ethereumjs-util'; import React, { useEffect, useState } from 'react'; import { View } from 'react-native'; import { useSelector } from 'react-redux'; -import { Hex } from '@metamask/utils'; +import { Hex, getChecksumAddress } from '@metamask/utils'; import { strings } from '../../../../../../../locales/i18n'; import AccountBalance from '../../../../../../component-library/components-temp/Accounts/AccountBalance'; @@ -58,7 +57,7 @@ const ApproveTransactionHeader = ({ const accountsByChainId = useSelector(selectAccountsByChainId); const internalAccounts = useSelector(selectInternalAccounts); - const activeAddress = toChecksumAddress(from); + const activeAddress = getChecksumAddress(from as Hex); const networkName = networkConfiguration?.name; diff --git a/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx b/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx index e71c8e594214..2f77cefcb8a3 100644 --- a/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx +++ b/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx @@ -5,7 +5,7 @@ import EthereumAddress from '../../../../../../UI/EthereumAddress'; import Engine from '../../../../../../../core/Engine'; import { MetaMetricsEvents } from '../../../../../../../core/Analytics'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress, Hex } from '@metamask/utils'; import { connect, useSelector } from 'react-redux'; import StyledButton from '../../../../../../UI/StyledButton'; import Text from '../../../../../../../component-library/components/Texts/Text'; @@ -120,7 +120,7 @@ const AddNickname = (props: AddNicknameProps) => { const { AddressBookController } = Engine.context as any; if (!newNickname || !address) return; AddressBookController.set( - toChecksumAddress(address), + getChecksumAddress(address as Hex), newNickname, providerChainId, ); diff --git a/app/components/hooks/useAccounts/useAccounts.test.ts b/app/components/hooks/useAccounts/useAccounts.test.ts index 8f78435a626a..928def44072f 100644 --- a/app/components/hooks/useAccounts/useAccounts.test.ts +++ b/app/components/hooks/useAccounts/useAccounts.test.ts @@ -1,12 +1,11 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { KeyringTypes } from '@metamask/keyring-controller'; import { EthScope } from '@metamask/keyring-api'; -import { toChecksumAddress } from 'ethereumjs-util'; import useAccounts from './useAccounts'; import { backgroundState } from '../../../util/test/initial-root-state'; import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; import { Account } from './useAccounts.types'; -import { Hex } from '@metamask/utils'; +import { Hex, getChecksumAddress } from '@metamask/utils'; // eslint-disable-next-line import/no-namespace import * as networks from '../../../util/networks'; @@ -32,7 +31,7 @@ const MOCK_ACCOUNTS = Object.values( const MOCK_ACCOUNT_1: Account = { id: MOCK_ACCOUNTS[0].id, name: 'Account 1', - address: toChecksumAddress(MOCK_ACCOUNTS[0].address) as Hex, + address: getChecksumAddress(MOCK_ACCOUNTS[0].address as Hex), type: KeyringTypes.hd, yOffset: 0, isSelected: false, @@ -47,7 +46,7 @@ const MOCK_ACCOUNT_1: Account = { const MOCK_ACCOUNT_2: Account = { id: MOCK_ACCOUNTS[1].id, name: 'Account 2', - address: toChecksumAddress(MOCK_ACCOUNTS[1].address) as Hex, + address: getChecksumAddress(MOCK_ACCOUNTS[1].address as Hex), type: KeyringTypes.hd, yOffset: 78, isSelected: true, diff --git a/app/components/hooks/useExistingAddress.ts b/app/components/hooks/useExistingAddress.ts index c67c2ab4b387..8ac396c4dccf 100644 --- a/app/components/hooks/useExistingAddress.ts +++ b/app/components/hooks/useExistingAddress.ts @@ -1,5 +1,5 @@ -import { toChecksumAddress } from 'ethereumjs-util'; import { useSelector } from 'react-redux'; +import { getChecksumAddress, Hex } from '@metamask/utils'; import { selectInternalAccounts } from '../../selectors/accountsController'; import { areAddressesEqual } from '../../util/address'; @@ -36,7 +36,7 @@ const useExistingAddress = (address?: string): AccountInfo | undefined => { if (!address || !isEvmSelected) return; // TODO: [SOLANA] Revisit this before shipping, Address Book controller should support non evm networks - const checksummedAddress = toChecksumAddress(address); + const checksummedAddress = getChecksumAddress(address as Hex); const matchingAddressBookEntry: AddressBookEntry | undefined = filteredAddressBook?.[checksummedAddress]; diff --git a/app/selectors/earnController/earn/index.ts b/app/selectors/earnController/earn/index.ts index a17c536ff068..e5a542b1fa67 100644 --- a/app/selectors/earnController/earn/index.ts +++ b/app/selectors/earnController/earn/index.ts @@ -3,7 +3,7 @@ import { selectLendingMarketsByChainIdAndOutputTokenAddress, selectLendingMarketsByChainIdAndTokenAddress, } from '@metamask/earn-controller'; -import { Hex } from '@metamask/utils'; +import { Hex, getChecksumAddress } from '@metamask/utils'; import BigNumber from 'bignumber.js'; import BN4 from 'bnjs4'; import { createSelector } from 'reselect'; @@ -28,7 +28,6 @@ import { selectNetworkConfigurations } from '../../networkController'; import { selectTokensBalances } from '../../tokenBalancesController'; import { selectTokenMarketData } from '../../tokenRatesController'; import { pooledStakingSelectors } from '../pooledStaking'; -import { toChecksumAddress } from 'ethereumjs-util'; import { selectPooledStakingEnabledFlag, selectStablecoinLendingEnabledFlag, @@ -194,12 +193,16 @@ const selectEarnTokens = createDeepEqualSelector( // TODO: balance logic, extract to utils then use when we are clear to add token const rawAccountBalance = selectedAddress ? accountsByChainId[token?.chainId as Hex]?.[ - isEvmAddress ? toChecksumAddress(selectedAddress) : selectedAddress + isEvmAddress + ? getChecksumAddress(selectedAddress as Hex) + : selectedAddress ]?.balance : '0'; const rawStakedAccountBalance = selectedAddress ? accountsByChainId[token?.chainId as Hex]?.[ - isEvmAddress ? toChecksumAddress(selectedAddress) : selectedAddress + isEvmAddress + ? getChecksumAddress(selectedAddress as Hex) + : selectedAddress ]?.stakedBalance : '0'; const balanceWei = hexToBN(rawAccountBalance); diff --git a/app/util/address/index.ts b/app/util/address/index.ts index 7aa4882be7bb..343f8a04cd91 100644 --- a/app/util/address/index.ts +++ b/app/util/address/index.ts @@ -1,10 +1,10 @@ import { - toChecksumAddress, isValidAddress, addHexPrefix, isValidChecksumAddress, isHexPrefixed, } from 'ethereumjs-util'; +import { getChecksumAddress, type Hex, isHexString } from '@metamask/utils'; import punycode from 'punycode/punycode'; import ExtendedKeyringTypes from '../../constants/keyringTypes'; import Engine from '../../core/Engine'; @@ -43,7 +43,6 @@ import type { NetworkState, } from '@metamask/network-controller'; import { KeyringTypes } from '@metamask/keyring-controller'; -import { type Hex, isHexString } from '@metamask/utils'; import PREINSTALLED_SNAPS from '../../lib/snaps/preinstalled-snaps'; import { EntropySourceId } from '@metamask/keyring-api'; @@ -310,9 +309,8 @@ export function getInternalAccountByAddress( address: string, ): InternalAccount | undefined { const { accounts } = Engine.context.AccountsController.state.internalAccounts; - return Object.values(accounts).find( - (a: InternalAccount) => - areAddressesEqual(a.address, address), + return Object.values(accounts).find((a: InternalAccount) => + areAddressesEqual(a.address, address), ); } @@ -322,17 +320,23 @@ export function getInternalAccountByAddress( * @param {String} address - String corresponding to an address * @returns {String} - Returns address's translated label text */ -export function getLabelTextByInternalAccount(internalAccount: InternalAccount) { +export function getLabelTextByInternalAccount( + internalAccount: InternalAccount, +) { const { KeyringController } = Engine.context; const { keyrings } = KeyringController.state; // Would be better if we have that mapping elsewhere, so we can index keyring by their // entropy source directly? - const hdKeyringsIndexByEntropySource: Map = new Map(); + const hdKeyringsIndexByEntropySource: Map = + new Map(); for (const keyring of keyrings) { if (keyring.type === ExtendedKeyringTypes.hd) { // Use the size of the map, so we don't need an extra index variable for this. - hdKeyringsIndexByEntropySource.set(keyring.metadata.id, hdKeyringsIndexByEntropySource.size); + hdKeyringsIndexByEntropySource.set( + keyring.metadata.id, + hdKeyringsIndexByEntropySource.size, + ); } } @@ -341,7 +345,8 @@ export function getLabelTextByInternalAccount(internalAccount: InternalAccount) const shouldShowSrpPill = hdKeyringsIndexByEntropySource.size > 1; if (shouldShowSrpPill && hdInternalAccount.options.entropySource) { - const entropySource = hdInternalAccount.options.entropySource as EntropySourceId; + const entropySource = hdInternalAccount.options + .entropySource as EntropySourceId; const hdKeyringIndex = hdKeyringsIndexByEntropySource.get(entropySource); if (hdKeyringIndex !== undefined) { @@ -474,7 +479,7 @@ export function resemblesAddress(address: string) { export function safeToChecksumAddress(address?: string) { if (!address) return undefined; - return toChecksumAddress(address) as Hex; + return getChecksumAddress(address as Hex); } /** @@ -607,7 +612,7 @@ export async function validateAddressOrENS( internalAccounts, ); } - const checksummedAddress = toChecksumAddress(toAccount); + const checksummedAddress = getChecksumAddress(toAccount as Hex); addressReady = true; const ens = await doENSReverseLookup(checksummedAddress); if (ens) { diff --git a/app/util/checkAddress.ts b/app/util/checkAddress.ts index 74c1d994a58b..4bd560382f81 100644 --- a/app/util/checkAddress.ts +++ b/app/util/checkAddress.ts @@ -1,5 +1,5 @@ import type { AddressBookControllerState } from '@metamask/address-book-controller'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress } from '@metamask/utils'; /** * Check whether the recipient of the given transaction is included in @@ -27,19 +27,19 @@ const checkIfAddressIsSaved = ( // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any const addressEntries = Object.values(chainAddresses).map((entry: any) => ({ - address: toChecksumAddress(entry.address), + address: getChecksumAddress(entry.address), nickname: entry.name, })); if ( addressEntries.some( (entry) => - entry.address === toChecksumAddress(transaction.to) && + entry.address === getChecksumAddress(transaction.to) && addressBookChainId === chainId, ) ) { return addressEntries.filter( - (entry) => entry.address === toChecksumAddress(transaction.to), + (entry) => entry.address === getChecksumAddress(transaction.to), ); } return []; diff --git a/app/util/transactions/index.js b/app/util/transactions/index.js index fabea1e47e99..e6cacef5bec2 100644 --- a/app/util/transactions/index.js +++ b/app/util/transactions/index.js @@ -1,4 +1,4 @@ -import { addHexPrefix, toChecksumAddress } from 'ethereumjs-util'; +import { getChecksumAddress, add0x } from '@metamask/utils'; import BN from 'bnjs4'; import { rawEncode, rawDecode } from 'ethereumjs-abi'; import BigNumber from 'bignumber.js'; @@ -227,7 +227,7 @@ export function generateTransferData(type = undefined, opts = {}) { .call( rawEncode( ['address', 'uint256'], - [opts.toAddress, addHexPrefix(opts.amount)], + [opts.toAddress, add0x(opts.amount)], ), (x) => ('00' + x.toString(16)).slice(-2), ) @@ -240,7 +240,7 @@ export function generateTransferData(type = undefined, opts = {}) { .call( rawEncode( ['address', 'address', 'uint256'], - [opts.fromAddress, opts.toAddress, addHexPrefix(opts.tokenId)], + [opts.fromAddress, opts.toAddress, add0x(opts.tokenId)], ), (x) => ('00' + x.toString(16)).slice(-2), ) @@ -296,9 +296,8 @@ export function generateApprovalData(opts) { return ( functionSignature + Array.prototype.map - .call( - rawEncode(['address', 'uint256'], [spender, addHexPrefix(value)]), - (x) => ('00' + x.toString(16)).slice(-2), + .call(rawEncode(['address', 'uint256'], [spender, add0x(value)]), (x) => + ('00' + x.toString(16)).slice(-2), ) .join('') ); @@ -306,7 +305,7 @@ export function generateApprovalData(opts) { export function decodeApproveData(data) { return { - spenderAddress: addHexPrefix(data.substr(34, 40)), + spenderAddress: add0x(data.substr(34, 40)), encodedAmount: data.substr(74, 138), }; } @@ -327,10 +326,10 @@ export function decodeTransferData(type, data) { const encodedAmount = data.substring(74, BASE + 74); const bufferEncodedAddress = rawEncode( ['address'], - [addHexPrefix(encodedAddress)], + [add0x(encodedAddress)], ); return [ - addHexPrefix(rawDecode(['address'], bufferEncodedAddress)[0]), + add0x(rawDecode(['address'], bufferEncodedAddress)[0]), parseInt(encodedAmount, 16).toString(), encodedAmount, ]; @@ -341,15 +340,15 @@ export function decodeTransferData(type, data) { const encodedTokenId = data.substring(138, BASE + 138); const bufferEncodedFromAddress = rawEncode( ['address'], - [addHexPrefix(encodedFromAddress)], + [add0x(encodedFromAddress)], ); const bufferEncodedToAddress = rawEncode( ['address'], - [addHexPrefix(encodedToAddress)], + [add0x(encodedToAddress)], ); return [ - addHexPrefix(rawDecode(['address'], bufferEncodedFromAddress)[0]), - addHexPrefix(rawDecode(['address'], bufferEncodedToAddress)[0]), + add0x(rawDecode(['address'], bufferEncodedFromAddress)[0]), + add0x(rawDecode(['address'], bufferEncodedToAddress)[0]), parseInt(encodedTokenId, 16).toString(), ]; } @@ -413,7 +412,7 @@ export async function isSmartContractAddress( ) { if (!address) return false; - address = toChecksumAddress(address); + address = getChecksumAddress(address); // If in contract map we don't need to cache it if ( @@ -645,11 +644,11 @@ export function getTransactionToName({ } const networkAddressBook = addressBook[chainId]; - const checksummedToAddress = toChecksumAddress(toAddress); + const checksummedToAddress = getChecksumAddress(toAddress); // Convert internalAccounts array to a map for quick lookup const internalAccountsMap = internalAccounts.reduce((acc, account) => { - acc[toChecksumAddress(account.address)] = account; + acc[getChecksumAddress(account.address)] = account; return acc; }, {}); From ac2aabaf5a0a3ce5c58a477eb7135e44be1416cf Mon Sep 17 00:00:00 2001 From: Nodonisko Date: Fri, 4 Jul 2025 21:56:16 +0200 Subject: [PATCH 2/7] chore: simplify address formatting --- app/selectors/earnController/earn/index.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/selectors/earnController/earn/index.ts b/app/selectors/earnController/earn/index.ts index e5a542b1fa67..0fc2811606cd 100644 --- a/app/selectors/earnController/earn/index.ts +++ b/app/selectors/earnController/earn/index.ts @@ -35,6 +35,7 @@ import { import { EarnTokenDetails } from '../../../components/UI/Earn/types/lending.types'; import { isNonEvmAddress } from '../../../core/Multichain/utils'; import { createDeepEqualSelector } from '../../util'; +import { toFormattedAddress } from '../../../util/address'; const selectEarnControllerState = (state: RootState) => state.engine.backgroundState.EarnController; @@ -191,19 +192,13 @@ const selectEarnTokens = createDeepEqualSelector( } // TODO: balance logic, extract to utils then use when we are clear to add token + const formattedAddress = toFormattedAddress(selectedAddress as Hex); const rawAccountBalance = selectedAddress - ? accountsByChainId[token?.chainId as Hex]?.[ - isEvmAddress - ? getChecksumAddress(selectedAddress as Hex) - : selectedAddress - ]?.balance + ? accountsByChainId[token?.chainId as Hex]?.[formattedAddress]?.balance : '0'; const rawStakedAccountBalance = selectedAddress - ? accountsByChainId[token?.chainId as Hex]?.[ - isEvmAddress - ? getChecksumAddress(selectedAddress as Hex) - : selectedAddress - ]?.stakedBalance + ? accountsByChainId[token?.chainId as Hex]?.[formattedAddress] + ?.stakedBalance : '0'; const balanceWei = hexToBN(rawAccountBalance); const stakedBalanceWei = hexToBN(rawStakedAccountBalance); From ee33b8635b18e3c0fc26cd5ab720fd72f8f03861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Such=C3=BD?= Date: Tue, 8 Jul 2025 16:14:44 +0200 Subject: [PATCH 3/7] fix: use correct checksum function --- app/components/Views/Settings/Contacts/ContactForm/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.js b/app/components/Views/Settings/Contacts/ContactForm/index.js index d5d945d2bb29..091ecaf4f9b5 100644 --- a/app/components/Views/Settings/Contacts/ContactForm/index.js +++ b/app/components/Views/Settings/Contacts/ContactForm/index.js @@ -335,7 +335,7 @@ class ContactForm extends PureComponent { if (wasChainIdChanged) { AddressBookController.delete( originalContactChainId, - toChecksumAddress(address), + getChecksumAddress(address), ); } From 5ecb2cb1cf5ee1a1975941f43e5ce904a799f888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Such=C3=BD?= Date: Tue, 8 Jul 2025 18:35:12 +0200 Subject: [PATCH 4/7] chore: remove unused vars and imports --- app/selectors/earnController/earn/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/selectors/earnController/earn/index.ts b/app/selectors/earnController/earn/index.ts index 0fc2811606cd..963823477c82 100644 --- a/app/selectors/earnController/earn/index.ts +++ b/app/selectors/earnController/earn/index.ts @@ -3,7 +3,7 @@ import { selectLendingMarketsByChainIdAndOutputTokenAddress, selectLendingMarketsByChainIdAndTokenAddress, } from '@metamask/earn-controller'; -import { Hex, getChecksumAddress } from '@metamask/utils'; +import { Hex } from '@metamask/utils'; import BigNumber from 'bignumber.js'; import BN4 from 'bnjs4'; import { createSelector } from 'reselect'; @@ -33,7 +33,6 @@ import { selectStablecoinLendingEnabledFlag, } from '../../../components/UI/Earn/selectors/featureFlags'; import { EarnTokenDetails } from '../../../components/UI/Earn/types/lending.types'; -import { isNonEvmAddress } from '../../../core/Multichain/utils'; import { createDeepEqualSelector } from '../../util'; import { toFormattedAddress } from '../../../util/address'; @@ -157,7 +156,6 @@ const selectEarnTokens = createDeepEqualSelector( selectLendingMarketsByChainIdAndTokenAddress(earnState); const lendingMarketsByChainIdAndOutputTokenAddress = selectLendingMarketsByChainIdAndOutputTokenAddress(earnState); - const isEvmAddress = !isNonEvmAddress(selectedAddress || ''); const earnTokensData = allTokens.reduce((acc, token) => { const experiences: EarnTokenDetails['experiences'] = []; From 04b84bcc508bcc6e98cb7e6e2ea597c7bba7c80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Such=C3=BD?= Date: Fri, 11 Jul 2025 14:48:12 +0200 Subject: [PATCH 5/7] chore: fix toChecksumAddress validation --- .../AggregatedPercentageCrossChains.test.tsx | 4 +- .../AggregatedPercentageCrossChains.tsx | 7 ++- .../UI/AccountFromToInfoCard/AddressFrom.tsx | 4 +- .../hooks/useTokensWithBalance/index.ts | 7 +-- .../UI/PaymentRequest/AssetList/index.tsx | 49 +++++++++++-------- app/components/UI/TransactionElement/utils.js | 4 +- .../Settings/Contacts/ContactForm/index.js | 6 +-- .../confirmations/hooks/useAccountInfo.ts | 6 +-- .../Views/confirmations/legacy/Send/index.js | 15 +++--- .../legacy/SendFlow/SendTo/index.js | 4 +- .../ApproveTransactionHeader.tsx | 5 +- .../AddNickname/index.tsx | 4 +- .../hooks/useAccounts/useAccounts.test.ts | 6 +-- app/components/hooks/useExistingAddress.ts | 5 +- app/util/address/index.test.ts | 11 +++++ app/util/address/index.ts | 29 +++++++++-- app/util/checkAddress.ts | 8 +-- app/util/transactions/index.js | 10 ++-- 18 files changed, 114 insertions(+), 70 deletions(-) diff --git a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx index 48d685bbd75c..02789d7362c3 100644 --- a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx +++ b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.test.tsx @@ -26,8 +26,8 @@ jest.mock('ethereumjs-util', () => ({ zeroAddress: jest.fn(() => '0x0000000000000000000000000000000000000000'), })); -jest.mock('@metamask/utils', () => ({ - getChecksumAddress: jest.fn((address) => address), +jest.mock('../../../../util/address', () => ({ + toChecksumAddress: jest.fn((address) => address), })); describe('AggregatedPercentageCrossChains', () => { diff --git a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx index 588ef361c842..9a839918e94e 100644 --- a/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx +++ b/app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx @@ -11,7 +11,6 @@ import { FORMATTED_PERCENTAGE_TEST_ID, } from './AggregatedPercentage.constants'; import { zeroAddress } from 'ethereumjs-util'; -import { getChecksumAddress, Hex } from '@metamask/utils'; import { selectTokenMarketData } from '../../../../selectors/tokenRatesController'; import { MarketDataMapping, @@ -19,6 +18,8 @@ import { } from '../../../../components/hooks/useGetFormattedTokensPerChain'; import { getFormattedAmountChange, getPercentageTextColor } from './utils'; import { AggregatedPercentageCrossChainsProps } from './AggregatedPercentageCrossChains.types'; +import { toChecksumAddress } from '../../../../util/address'; + export const getCalculatedTokenAmount1dAgo = ( tokenFiatBalance: number, tokenPricePercentChange1dAgo: number, @@ -48,9 +49,7 @@ const AggregatedPercentageCrossChains = ({ const totalPerChain1dAgoERC20 = tokensWithBalances.reduce( (total1dAgo: number, item: { address: string }, idx: number) => { const found = - crossChainMarketData?.[chainId]?.[ - getChecksumAddress(item.address as Hex) - ]; + crossChainMarketData?.[chainId]?.[toChecksumAddress(item.address)]; const tokenFiat1dAgo = getCalculatedTokenAmount1dAgo( tokenFiatBalances[idx], diff --git a/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx b/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx index 45dc3f614c81..0f215fbbf1c0 100644 --- a/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx +++ b/app/components/UI/AccountFromToInfoCard/AddressFrom.tsx @@ -1,4 +1,3 @@ -import { getChecksumAddress, Hex } from '@metamask/utils'; import React, { useEffect, useState } from 'react'; import { View } from 'react-native'; import { useSelector } from 'react-redux'; @@ -16,6 +15,7 @@ import { import { getLabelTextByAddress, renderAccountName, + toChecksumAddress, } from '../../../util/address'; import useAddressBalance from '../../hooks/useAddressBalance/useAddressBalance'; import stylesheet from './AddressFrom.styles'; @@ -62,7 +62,7 @@ const AddressFrom = ({ const accountsByChainId = useSelector(selectAccountsByChainId); const internalAccounts = useSelector(selectInternalEvmAccounts); - const activeAddress = getChecksumAddress(from as Hex); + const activeAddress = toChecksumAddress(from); const networkName = useSelector(selectEvmNetworkName); const networkImage = useSelector(selectEvmNetworkImageSource); diff --git a/app/components/UI/Bridge/hooks/useTokensWithBalance/index.ts b/app/components/UI/Bridge/hooks/useTokensWithBalance/index.ts index fbfc147a4529..7dd8834a97cd 100644 --- a/app/components/UI/Bridge/hooks/useTokensWithBalance/index.ts +++ b/app/components/UI/Bridge/hooks/useTokensWithBalance/index.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { useSelector } from 'react-redux'; -import { CaipChainId, getChecksumAddress, Hex } from '@metamask/utils'; +import { CaipChainId, Hex } from '@metamask/utils'; import { TokenI } from '../../../Tokens/types'; import { selectTokensBalances } from '../../../../../selectors/tokenBalancesController'; import { @@ -30,6 +30,7 @@ import { renderNumber, renderFiat } from '../../../../../util/number'; import { formatUnits } from 'ethers/lib/utils'; import { BigNumber } from 'ethers'; import { selectAccountsByChainId } from '../../../../../selectors/accountTrackerController'; +import { toChecksumAddress } from '../../../../../util/address'; interface CalculateFiatBalancesParams { assets: TokenI[]; @@ -90,7 +91,7 @@ export const calculateEvmBalances = ({ // Native EVM token if (token.isETH || token.isNative) { const nativeTokenBalanceAtomicHex = - evmAccountsByChainId?.[chainId]?.[getChecksumAddress(selectedAddress)] + evmAccountsByChainId?.[chainId]?.[toChecksumAddress(selectedAddress)] ?.balance || '0x0'; const nativeTokenBalance = formatUnits( BigNumber.from(nativeTokenBalanceAtomicHex), @@ -116,7 +117,7 @@ export const calculateEvmBalances = ({ ); const erc20BalanceAtomicHex = multiChainTokenBalances?.[token.address as Hex] || - multiChainTokenBalances?.[getChecksumAddress(token.address as Hex)] || + multiChainTokenBalances?.[toChecksumAddress(token.address as Hex)] || '0x0'; const erc20Balance = formatUnits( diff --git a/app/components/UI/PaymentRequest/AssetList/index.tsx b/app/components/UI/PaymentRequest/AssetList/index.tsx index 8921c3abe196..2c2aad5927a1 100644 --- a/app/components/UI/PaymentRequest/AssetList/index.tsx +++ b/app/components/UI/PaymentRequest/AssetList/index.tsx @@ -6,10 +6,10 @@ import { fontStyles } from '../../../../styles/common'; import Identicon from '../../Identicon'; import NetworkMainAssetLogo from '../../NetworkMainAssetLogo'; import { useSelector } from 'react-redux'; -import { getChecksumAddress } from '@metamask/utils'; import { useTheme } from '../../../../util/theme'; import { selectTokenList } from '../../../../selectors/tokenListController'; import { ImportTokenViewSelectorsIDs } from '../../../../../e2e/selectors/wallet/ImportTokenView.selectors'; +import { toChecksumAddress } from '../../../../util/address'; // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -103,7 +103,7 @@ const AssetList = ({ return ; } const token = - tokenList?.[getChecksumAddress(address)] || + tokenList?.[toChecksumAddress(address)] || tokenList?.[address.toLowerCase()]; const iconUrl = token?.iconUrl; if (!iconUrl) { @@ -124,25 +124,32 @@ const AssetList = ({ style={styles.listContainer} > {/* Use simple rendering like token import for better performance */} - {searchResults.slice(0, 6).map((item: { symbol?: string; name?: string; address?: string }, index: number) => { - const { symbol, name } = item || {}; - return ( - handleSelectAsset(item)} - > - - {renderLogo(item)} - - {symbol} - {!!name && {name}} - - - - ); - })} + {searchResults + .slice(0, 6) + .map( + ( + item: { symbol?: string; name?: string; address?: string }, + index: number, + ) => { + const { symbol, name } = item || {}; + return ( + handleSelectAsset(item)} + > + + {renderLogo(item)} + + {symbol} + {!!name && {name}} + + + + ); + }, + )} ); }; diff --git a/app/components/UI/TransactionElement/utils.js b/app/components/UI/TransactionElement/utils.js index 2962dd8541c6..7970efaf4ef8 100644 --- a/app/components/UI/TransactionElement/utils.js +++ b/app/components/UI/TransactionElement/utils.js @@ -16,6 +16,7 @@ import { renderFullAddress, areAddressesEqual, toFormattedAddress, + toChecksumAddress, } from '../../../util/address'; import { decodeTransferData, @@ -24,7 +25,6 @@ import { getActionKey, TRANSACTION_TYPES, } from '../../../util/transactions'; -import { getChecksumAddress } from '@metamask/utils'; import { swapsUtils } from '@metamask/swaps-controller'; import { isSwapsNativeAsset } from '../Swaps/utils'; import Engine from '../../../core/Engine'; @@ -242,7 +242,7 @@ export function decodeIncomingTransfer(args) { : undefined; const exchangeRate = token && contractExchangeRates - ? contractExchangeRates[getChecksumAddress(token.address)]?.price + ? contractExchangeRates[toChecksumAddress(token.address)]?.price : undefined; let renderTokenFiatAmount, renderTokenFiatNumber; diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.js b/app/components/Views/Settings/Contacts/ContactForm/index.js index 091ecaf4f9b5..592b64af9bd9 100644 --- a/app/components/Views/Settings/Contacts/ContactForm/index.js +++ b/app/components/Views/Settings/Contacts/ContactForm/index.js @@ -13,7 +13,7 @@ import PropTypes from 'prop-types'; import { getEditableOptions } from '../../../../UI/Navbar'; import StyledButton from '../../../../UI/StyledButton'; import Engine from '../../../../../core/Engine'; -import { getChecksumAddress } from '@metamask/utils'; +import { toChecksumAddress } from '@metamask/utils'; import { connect } from 'react-redux'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { strings } from '../../../../../../locales/i18n'; @@ -335,12 +335,12 @@ class ContactForm extends PureComponent { if (wasChainIdChanged) { AddressBookController.delete( originalContactChainId, - getChecksumAddress(address), + toChecksumAddress(address), ); } AddressBookController.set( - getChecksumAddress(toEnsAddress || address), + toChecksumAddress(toEnsAddress || address), name, contactChainId || chainId, memo, diff --git a/app/components/Views/confirmations/hooks/useAccountInfo.ts b/app/components/Views/confirmations/hooks/useAccountInfo.ts index fd50a3dd4ab9..19e23088fc1d 100644 --- a/app/components/Views/confirmations/hooks/useAccountInfo.ts +++ b/app/components/Views/confirmations/hooks/useAccountInfo.ts @@ -1,18 +1,18 @@ import { useMemo } from 'react'; import { useSelector } from 'react-redux'; -import { Hex, getChecksumAddress } from '@metamask/utils'; +import { Hex } from '@metamask/utils'; import Engine from '../../../../core/Engine'; import useAddressBalance from '../../../../components/hooks/useAddressBalance/useAddressBalance'; import { selectInternalAccounts } from '../../../../selectors/accountsController'; -import { renderAccountName } from '../../../../util/address'; +import { renderAccountName, toChecksumAddress } from '../../../../util/address'; import { selectCurrentCurrency } from '../../../../selectors/currencyRateController'; import { formatWithThreshold } from '../../../../util/assets'; import I18n from '../../../../../locales/i18n'; const useAccountInfo = (address: string, chainId: Hex) => { const internalAccounts = useSelector(selectInternalAccounts); - const activeAddress = getChecksumAddress(address as Hex); + const activeAddress = toChecksumAddress(address as Hex); const { addressBalance: accountBalance } = useAddressBalance( undefined, address, diff --git a/app/components/Views/confirmations/legacy/Send/index.js b/app/components/Views/confirmations/legacy/Send/index.js index 47f6d63e756b..63a8faa5189a 100644 --- a/app/components/Views/confirmations/legacy/Send/index.js +++ b/app/components/Views/confirmations/legacy/Send/index.js @@ -17,7 +17,6 @@ import { fromWei, fromTokenMinimalUnit, } from '../../../../../util/number'; -import { getChecksumAddress } from '@metamask/utils'; import { strings } from '../../../../../../locales/i18n'; import { getTransactionOptionsTitle } from '../../../../UI/Navbar'; import { connect } from 'react-redux'; @@ -36,7 +35,11 @@ import { generateTransferData, } from '../../../../../util/transactions'; import Logger from '../../../../../util/Logger'; -import { getAddress, areAddressesEqual } from '../../../../../util/address'; +import { + getAddress, + areAddressesEqual, + toChecksumAddress, +} from '../../../../../util/address'; import { MAINNET } from '../../../../../constants/network'; import BigNumber from 'bignumber.js'; import { WalletDevice } from '@metamask/transaction-controller'; @@ -443,7 +446,7 @@ class Send extends PureComponent { */ handleTokenDeeplink = async (address) => { const { tokens, tokenList } = this.props; - address = getChecksumAddress(address); + address = toChecksumAddress(address); // First check if we have token information in token list if (address in tokenList) { return tokenList[address]; @@ -582,7 +585,7 @@ class Send extends PureComponent { let checksummedAddress = null; if (assetType === 'ETH') { - checksummedAddress = getChecksumAddress(transactionMeta.transaction.to); + checksummedAddress = toChecksumAddress(transactionMeta.transaction.to); } else if (assetType === 'ERC20') { try { const [addressTo] = decodeTransferData( @@ -590,7 +593,7 @@ class Send extends PureComponent { transactionMeta.transaction.data, ); if (addressTo) { - checksummedAddress = getChecksumAddress(addressTo); + checksummedAddress = toChecksumAddress(addressTo); } } catch (e) { Logger.log('Error decoding transfer data', transactionMeta.data); @@ -603,7 +606,7 @@ class Send extends PureComponent { ); const addressTo = data[1]; if (addressTo) { - checksummedAddress = getChecksumAddress(addressTo); + checksummedAddress = toChecksumAddress(addressTo); } } catch (e) { Logger.log('Error decoding transfer data', transactionMeta.data); diff --git a/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js b/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js index ba8ab2aac3d3..7a559c534b08 100644 --- a/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js +++ b/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js @@ -2,7 +2,7 @@ import React, { Fragment, PureComponent } from 'react'; import { View, ScrollView, Alert, Platform, BackHandler } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { getChecksumAddress } from '@metamask/utils'; +import { toChecksumAddress } from '@metamask/utils'; import { SafeAreaView } from 'react-native-safe-area-context'; import Icon from 'react-native-vector-icons/FontAwesome'; import AddressList from '../AddressList'; @@ -490,7 +490,7 @@ class SendFlow extends PureComponent { safeChecksumAddress = (address) => { try { - return getChecksumAddress(address); + return toChecksumAddress(address); } catch (error) { return address; } diff --git a/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx b/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx index dfa8409d653c..a17543cf0b80 100644 --- a/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx +++ b/app/components/Views/confirmations/legacy/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { View } from 'react-native'; import { useSelector } from 'react-redux'; -import { Hex, getChecksumAddress } from '@metamask/utils'; +import { Hex } from '@metamask/utils'; import { strings } from '../../../../../../../locales/i18n'; import AccountBalance from '../../../../../../component-library/components-temp/Accounts/AccountBalance'; @@ -11,6 +11,7 @@ import { selectAccountsByChainId } from '../../../../../../selectors/accountTrac import { getLabelTextByAddress, renderAccountName, + toChecksumAddress, } from '../../../../../../util/address'; import useAddressBalance from '../../../../../hooks/useAddressBalance/useAddressBalance'; import { @@ -57,7 +58,7 @@ const ApproveTransactionHeader = ({ const accountsByChainId = useSelector(selectAccountsByChainId); const internalAccounts = useSelector(selectInternalAccounts); - const activeAddress = getChecksumAddress(from as Hex); + const activeAddress = toChecksumAddress(from); const networkName = networkConfiguration?.name; diff --git a/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx b/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx index 2f77cefcb8a3..d713b17a2df3 100644 --- a/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx +++ b/app/components/Views/confirmations/legacy/components/ApproveTransactionReview/AddNickname/index.tsx @@ -5,7 +5,6 @@ import EthereumAddress from '../../../../../../UI/EthereumAddress'; import Engine from '../../../../../../../core/Engine'; import { MetaMetricsEvents } from '../../../../../../../core/Analytics'; -import { getChecksumAddress, Hex } from '@metamask/utils'; import { connect, useSelector } from 'react-redux'; import StyledButton from '../../../../../../UI/StyledButton'; import Text from '../../../../../../../component-library/components/Texts/Text'; @@ -24,6 +23,7 @@ import { AddNicknameProps } from './types'; import { validateAddressOrENS, shouldShowBlockExplorer, + toChecksumAddress, } from '../../../../../../../util/address'; import ErrorMessage from '../../../SendFlow/ErrorMessage'; import { @@ -120,7 +120,7 @@ const AddNickname = (props: AddNicknameProps) => { const { AddressBookController } = Engine.context as any; if (!newNickname || !address) return; AddressBookController.set( - getChecksumAddress(address as Hex), + toChecksumAddress(address), newNickname, providerChainId, ); diff --git a/app/components/hooks/useAccounts/useAccounts.test.ts b/app/components/hooks/useAccounts/useAccounts.test.ts index 928def44072f..6b43597ae0b8 100644 --- a/app/components/hooks/useAccounts/useAccounts.test.ts +++ b/app/components/hooks/useAccounts/useAccounts.test.ts @@ -5,9 +5,9 @@ import useAccounts from './useAccounts'; import { backgroundState } from '../../../util/test/initial-root-state'; import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; import { Account } from './useAccounts.types'; -import { Hex, getChecksumAddress } from '@metamask/utils'; // eslint-disable-next-line import/no-namespace import * as networks from '../../../util/networks'; +import { toChecksumAddress } from '../../../util/address'; jest.mock('../../../core/Engine', () => ({ getTotalEvmFiatAccountBalance: jest.fn().mockReturnValue({ @@ -31,7 +31,7 @@ const MOCK_ACCOUNTS = Object.values( const MOCK_ACCOUNT_1: Account = { id: MOCK_ACCOUNTS[0].id, name: 'Account 1', - address: getChecksumAddress(MOCK_ACCOUNTS[0].address as Hex), + address: toChecksumAddress(MOCK_ACCOUNTS[0].address), type: KeyringTypes.hd, yOffset: 0, isSelected: false, @@ -46,7 +46,7 @@ const MOCK_ACCOUNT_1: Account = { const MOCK_ACCOUNT_2: Account = { id: MOCK_ACCOUNTS[1].id, name: 'Account 2', - address: getChecksumAddress(MOCK_ACCOUNTS[1].address as Hex), + address: toChecksumAddress(MOCK_ACCOUNTS[1].address), type: KeyringTypes.hd, yOffset: 78, isSelected: true, diff --git a/app/components/hooks/useExistingAddress.ts b/app/components/hooks/useExistingAddress.ts index 8ac396c4dccf..100055af144b 100644 --- a/app/components/hooks/useExistingAddress.ts +++ b/app/components/hooks/useExistingAddress.ts @@ -1,8 +1,7 @@ import { useSelector } from 'react-redux'; -import { getChecksumAddress, Hex } from '@metamask/utils'; import { selectInternalAccounts } from '../../selectors/accountsController'; -import { areAddressesEqual } from '../../util/address'; +import { areAddressesEqual, toChecksumAddress } from '../../util/address'; import { AddressBookEntry } from '@metamask/address-book-controller'; import { selectAddressBook } from '../../selectors/addressBookController'; import { selectIsEvmNetworkSelected } from '../../selectors/multichainNetworkController'; @@ -36,7 +35,7 @@ const useExistingAddress = (address?: string): AccountInfo | undefined => { if (!address || !isEvmSelected) return; // TODO: [SOLANA] Revisit this before shipping, Address Book controller should support non evm networks - const checksummedAddress = getChecksumAddress(address as Hex); + const checksummedAddress = toChecksumAddress(address); const matchingAddressBookEntry: AddressBookEntry | undefined = filteredAddressBook?.[checksummedAddress]; diff --git a/app/util/address/index.test.ts b/app/util/address/index.test.ts index cc2931f23229..6e12ab83152a 100644 --- a/app/util/address/index.test.ts +++ b/app/util/address/index.test.ts @@ -22,6 +22,7 @@ import { renderAccountName, getTokenDetails, areAddressesEqual, + toChecksumAddress, } from '.'; import { mockHDKeyringAddress, @@ -534,6 +535,16 @@ describe('resemblesAddress', () => { expect(resemblesAddress(mockHDKeyringAddress)).toBeTruthy(); }); }); +describe('toChecksumAddress', () => { + it('should return the same address if it is invalid hex string', () => { + expect(toChecksumAddress('0x1')).toBe('0x1'); + expect(toChecksumAddress('0x123')).toBe('0x123'); + expect(toChecksumAddress('0x1234')).toBe('0x1234'); + }); + it('should throw an error if address is not a hex string', () => { + expect(() => toChecksumAddress('abc')).toThrow('Invalid hex address.'); + }); +}); describe('isSnapAccount,', () => { it('should return true if account is of type Snap Keyring', () => { expect(isSnapAccount(mockSnapAddress1)).toBeTruthy(); diff --git a/app/util/address/index.ts b/app/util/address/index.ts index 343f8a04cd91..c420f579416d 100644 --- a/app/util/address/index.ts +++ b/app/util/address/index.ts @@ -4,7 +4,12 @@ import { isValidChecksumAddress, isHexPrefixed, } from 'ethereumjs-util'; -import { getChecksumAddress, type Hex, isHexString } from '@metamask/utils'; +import { + getChecksumAddress, + type Hex, + isHexString, + isStrictHexString, +} from '@metamask/utils'; import punycode from 'punycode/punycode'; import ExtendedKeyringTypes from '../../constants/keyringTypes'; import Engine from '../../core/Engine'; @@ -477,9 +482,27 @@ export function resemblesAddress(address: string) { return address && address.length === 2 + 20 * 2; } +export function toChecksumAddress(address: string) { + try { + return getChecksumAddress(address as Hex); + } catch (error) { + // This is necessary for backward compatibility with the old behavior of + // `ethereumjs-util` which would return the original string if the address + // was invalid. This should happen only in tests which uses invalid addresses + // like 0x1, 0x2 etc. + if (error instanceof Error && error.message === 'Invalid hex address.') { + if (isStrictHexString(address)) { + return address as Hex; + } + throw new Error('Invalid hex address.'); + } + throw error; + } +} + export function safeToChecksumAddress(address?: string) { if (!address) return undefined; - return getChecksumAddress(address as Hex); + return toChecksumAddress(address); } /** @@ -612,7 +635,7 @@ export async function validateAddressOrENS( internalAccounts, ); } - const checksummedAddress = getChecksumAddress(toAccount as Hex); + const checksummedAddress = toChecksumAddress(toAccount); addressReady = true; const ens = await doENSReverseLookup(checksummedAddress); if (ens) { diff --git a/app/util/checkAddress.ts b/app/util/checkAddress.ts index 4bd560382f81..bca965be1415 100644 --- a/app/util/checkAddress.ts +++ b/app/util/checkAddress.ts @@ -1,5 +1,5 @@ import type { AddressBookControllerState } from '@metamask/address-book-controller'; -import { getChecksumAddress } from '@metamask/utils'; +import { toChecksumAddress } from './address'; /** * Check whether the recipient of the given transaction is included in @@ -27,19 +27,19 @@ const checkIfAddressIsSaved = ( // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any const addressEntries = Object.values(chainAddresses).map((entry: any) => ({ - address: getChecksumAddress(entry.address), + address: toChecksumAddress(entry.address), nickname: entry.name, })); if ( addressEntries.some( (entry) => - entry.address === getChecksumAddress(transaction.to) && + entry.address === toChecksumAddress(transaction.to) && addressBookChainId === chainId, ) ) { return addressEntries.filter( - (entry) => entry.address === getChecksumAddress(transaction.to), + (entry) => entry.address === toChecksumAddress(transaction.to), ); } return []; diff --git a/app/util/transactions/index.js b/app/util/transactions/index.js index e6cacef5bec2..598eeb1f37fb 100644 --- a/app/util/transactions/index.js +++ b/app/util/transactions/index.js @@ -1,4 +1,4 @@ -import { getChecksumAddress, add0x } from '@metamask/utils'; +import { add0x } from '@metamask/utils'; import BN from 'bnjs4'; import { rawEncode, rawDecode } from 'ethereumjs-abi'; import BigNumber from 'bignumber.js'; @@ -17,7 +17,7 @@ import { import { swapsUtils } from '@metamask/swaps-controller'; import Engine from '../../core/Engine'; import I18n, { strings } from '../../../locales/i18n'; -import { safeToChecksumAddress } from '../address'; +import { safeToChecksumAddress, toChecksumAddress } from '../address'; import { balanceToFiatNumber, BNToHex, @@ -412,7 +412,7 @@ export async function isSmartContractAddress( ) { if (!address) return false; - address = getChecksumAddress(address); + address = toChecksumAddress(address); // If in contract map we don't need to cache it if ( @@ -644,11 +644,11 @@ export function getTransactionToName({ } const networkAddressBook = addressBook[chainId]; - const checksummedToAddress = getChecksumAddress(toAddress); + const checksummedToAddress = toChecksumAddress(toAddress); // Convert internalAccounts array to a map for quick lookup const internalAccountsMap = internalAccounts.reduce((acc, account) => { - acc[getChecksumAddress(account.address)] = account; + acc[toChecksumAddress(account.address)] = account; return acc; }, {}); From 66ce3ab20113d06fb71cc4cd6dfc92cd891ba2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Such=C3=BD?= Date: Mon, 14 Jul 2025 18:16:44 +0200 Subject: [PATCH 6/7] fix: use correct addHex function --- app/util/transactions/index.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/app/util/transactions/index.js b/app/util/transactions/index.js index 598eeb1f37fb..27d5092ce5f7 100644 --- a/app/util/transactions/index.js +++ b/app/util/transactions/index.js @@ -1,4 +1,4 @@ -import { add0x } from '@metamask/utils'; +import { addHexPrefix } from 'ethereumjs-util'; import BN from 'bnjs4'; import { rawEncode, rawDecode } from 'ethereumjs-abi'; import BigNumber from 'bignumber.js'; @@ -227,7 +227,7 @@ export function generateTransferData(type = undefined, opts = {}) { .call( rawEncode( ['address', 'uint256'], - [opts.toAddress, add0x(opts.amount)], + [opts.toAddress, addHexPrefix(opts.amount)], ), (x) => ('00' + x.toString(16)).slice(-2), ) @@ -240,7 +240,7 @@ export function generateTransferData(type = undefined, opts = {}) { .call( rawEncode( ['address', 'address', 'uint256'], - [opts.fromAddress, opts.toAddress, add0x(opts.tokenId)], + [opts.fromAddress, opts.toAddress, addHexPrefix(opts.tokenId)], ), (x) => ('00' + x.toString(16)).slice(-2), ) @@ -296,8 +296,9 @@ export function generateApprovalData(opts) { return ( functionSignature + Array.prototype.map - .call(rawEncode(['address', 'uint256'], [spender, add0x(value)]), (x) => - ('00' + x.toString(16)).slice(-2), + .call( + rawEncode(['address', 'uint256'], [spender, addHexPrefix(value)]), + (x) => ('00' + x.toString(16)).slice(-2), ) .join('') ); @@ -305,7 +306,7 @@ export function generateApprovalData(opts) { export function decodeApproveData(data) { return { - spenderAddress: add0x(data.substr(34, 40)), + spenderAddress: addHexPrefix(data.substr(34, 40)), encodedAmount: data.substr(74, 138), }; } @@ -326,10 +327,10 @@ export function decodeTransferData(type, data) { const encodedAmount = data.substring(74, BASE + 74); const bufferEncodedAddress = rawEncode( ['address'], - [add0x(encodedAddress)], + [addHexPrefix(encodedAddress)], ); return [ - add0x(rawDecode(['address'], bufferEncodedAddress)[0]), + addHexPrefix(rawDecode(['address'], bufferEncodedAddress)[0]), parseInt(encodedAmount, 16).toString(), encodedAmount, ]; @@ -340,15 +341,15 @@ export function decodeTransferData(type, data) { const encodedTokenId = data.substring(138, BASE + 138); const bufferEncodedFromAddress = rawEncode( ['address'], - [add0x(encodedFromAddress)], + [addHexPrefix(encodedFromAddress)], ); const bufferEncodedToAddress = rawEncode( ['address'], - [add0x(encodedToAddress)], + [addHexPrefix(encodedToAddress)], ); return [ - add0x(rawDecode(['address'], bufferEncodedFromAddress)[0]), - add0x(rawDecode(['address'], bufferEncodedToAddress)[0]), + addHexPrefix(rawDecode(['address'], bufferEncodedFromAddress)[0]), + addHexPrefix(rawDecode(['address'], bufferEncodedToAddress)[0]), parseInt(encodedTokenId, 16).toString(), ]; } From 5e9bd81357cdef750d68bb1eb0d92a9c3c3a8746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Such=C3=BD?= Date: Mon, 14 Jul 2025 18:55:15 +0200 Subject: [PATCH 7/7] fix: fix bad imports in some js files --- app/components/Views/Settings/Contacts/ContactForm/index.js | 2 +- .../Views/confirmations/legacy/SendFlow/SendTo/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.js b/app/components/Views/Settings/Contacts/ContactForm/index.js index 592b64af9bd9..f5cab196a0ba 100644 --- a/app/components/Views/Settings/Contacts/ContactForm/index.js +++ b/app/components/Views/Settings/Contacts/ContactForm/index.js @@ -13,7 +13,6 @@ import PropTypes from 'prop-types'; import { getEditableOptions } from '../../../../UI/Navbar'; import StyledButton from '../../../../UI/StyledButton'; import Engine from '../../../../../core/Engine'; -import { toChecksumAddress } from '@metamask/utils'; import { connect } from 'react-redux'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { strings } from '../../../../../../locales/i18n'; @@ -21,6 +20,7 @@ import { renderShortAddress, areAddressesEqual, validateAddressOrENS, + toChecksumAddress, } from '../../../../../util/address'; import ErrorMessage from '../../../confirmations/legacy/SendFlow/ErrorMessage'; import AntIcon from 'react-native-vector-icons/AntDesign'; diff --git a/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js b/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js index 7a559c534b08..92170aadefef 100644 --- a/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js +++ b/app/components/Views/confirmations/legacy/SendFlow/SendTo/index.js @@ -2,7 +2,6 @@ import React, { Fragment, PureComponent } from 'react'; import { View, ScrollView, Alert, Platform, BackHandler } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { toChecksumAddress } from '@metamask/utils'; import { SafeAreaView } from 'react-native-safe-area-context'; import Icon from 'react-native-vector-icons/FontAwesome'; import AddressList from '../AddressList'; @@ -21,6 +20,7 @@ import { isValidHexAddress, validateAddressOrENS, areAddressesEqual, + toChecksumAddress, } from '../../../../../../util/address'; import { getEther, getTicker } from '../../../../../../util/transactions'; import {