Skip to content

Commit 54e9abd

Browse files
fix: 15108 create a wrapper for toChecksumAddress to prevent app crash (#15202)
…dress validation <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Related issues** Fixes: #15108 ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Nico MASSART <NicolasMassart@users.noreply.github.com>
1 parent 7a37e0a commit 54e9abd

File tree

26 files changed

+133
-80
lines changed

26 files changed

+133
-80
lines changed

app/component-library/components-temp/Price/AggregatedPercentage/AggregatedPercentageCrossChains.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import {
1010
FORMATTED_VALUE_PRICE_TEST_ID,
1111
FORMATTED_PERCENTAGE_TEST_ID,
1212
} from './AggregatedPercentage.constants';
13-
import { toChecksumAddress, zeroAddress } from 'ethereumjs-util';
13+
import { zeroAddress } from 'ethereumjs-util';
1414
import { selectTokenMarketData } from '../../../../selectors/tokenRatesController';
1515
import {
1616
MarketDataMapping,
1717
TokensWithBalances,
1818
} from '../../../../components/hooks/useGetFormattedTokensPerChain';
1919
import { getFormattedAmountChange, getPercentageTextColor } from './utils';
2020
import { AggregatedPercentageCrossChainsProps } from './AggregatedPercentageCrossChains.types';
21+
import { safeToChecksumAddress } from '../../../../util/address';
2122

2223
export const getCalculatedTokenAmount1dAgo = (
2324
tokenFiatBalance: number,
@@ -47,12 +48,14 @@ const AggregatedPercentageCrossChains = ({
4748
) => {
4849
const totalPerChain1dAgoERC20 = tokensWithBalances.reduce(
4950
(total1dAgo: number, item: { address: string }, idx: number) => {
50-
const found =
51-
crossChainMarketData?.[chainId]?.[toChecksumAddress(item.address)];
51+
const checksumAddress = item.address ? safeToChecksumAddress(item.address) : undefined;
52+
const found = checksumAddress !== undefined
53+
? crossChainMarketData?.[chainId]?.[checksumAddress]
54+
: undefined;
5255

5356
const tokenFiat1dAgo = getCalculatedTokenAmount1dAgo(
5457
tokenFiatBalances[idx],
55-
found?.pricePercentChange1d,
58+
found?.pricePercentChange1d ?? 0,
5659
);
5760
return total1dAgo + Number(tokenFiat1dAgo);
5861
},

app/components/UI/AccountFromToInfoCard/AddressFrom.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { toChecksumAddress } from 'ethereumjs-util';
21
import React, { useEffect, useState } from 'react';
32
import { View } from 'react-native';
43
import { useSelector } from 'react-redux';
@@ -16,6 +15,7 @@ import {
1615
import {
1716
getLabelTextByAddress,
1817
renderAccountName,
18+
safeToChecksumAddress,
1919
} from '../../../util/address';
2020
import useAddressBalance from '../../hooks/useAddressBalance/useAddressBalance';
2121
import stylesheet from './AddressFrom.styles';
@@ -53,7 +53,7 @@ const AddressFrom = ({
5353
const accountsByChainId = useSelector(selectAccountsByChainId);
5454

5555
const internalAccounts = useSelector(selectInternalAccounts);
56-
const activeAddress = toChecksumAddress(from);
56+
const activeAddress = safeToChecksumAddress(from);
5757

5858
const networkName = useSelector(selectEvmNetworkName);
5959

app/components/UI/AddCustomCollectible/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Alert, Text, TextInput, View, StyleSheet } from 'react-native';
44
import { fontStyles } from '../../../styles/common';
55
import Engine from '../../../core/Engine';
66
import { strings } from '../../../../locales/i18n';
7-
import { isValidAddress } from 'ethereumjs-util';
87
import ActionView from '../ActionView';
98
import { isSmartContractAddress } from '../../../util/transactions';
109
import Device from '../../../util/device';
@@ -16,6 +15,7 @@ import { selectChainId } from '../../../selectors/networkController';
1615
import { selectSelectedInternalAccountFormattedAddress } from '../../../selectors/accountsController';
1716
import { getDecimalChainId } from '../../../util/networks';
1817
import { useMetrics } from '../../../components/hooks/useMetrics';
18+
import { isValidHexAddress } from '../../../util/address';
1919

2020
// TODO: Replace "any" with type
2121
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -113,7 +113,7 @@ const AddCustomCollectible = ({
113113

114114
const validateCustomCollectibleAddress = async (): Promise<boolean> => {
115115
let validated = true;
116-
const isValidEthAddress = isValidAddress(address);
116+
const isValidEthAddress = isValidHexAddress(address);
117117
if (address.length === 0) {
118118
setWarningAddress(strings('collectible.address_cant_be_empty'));
119119
validated = false;

app/components/UI/AddCustomToken/index.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@ import {
55
View,
66
StyleSheet,
77
InteractionManager,
8-
Platform,
98
ScrollView,
109
} from 'react-native';
1110
import { fontStyles } from '../../../styles/common';
1211
import Engine from '../../../core/Engine';
1312
import PropTypes from 'prop-types';
1413
import { strings } from '../../../../locales/i18n';
15-
import { isValidAddress } from 'ethereumjs-util';
1614
import { isSmartContractAddress } from '../../../util/transactions';
1715
import { MetaMetricsEvents } from '../../../core/Analytics';
1816

@@ -43,6 +41,7 @@ import Banner, {
4341
} from '../../../component-library/components/Banners/Banner';
4442
import CLText from '../../../component-library/components/Texts/Text/Text';
4543
import Logger from '../../../util/Logger';
44+
import { isValidHexAddress } from '../../../util/address';
4645

4746
const createStyles = (colors) =>
4847
StyleSheet.create({
@@ -280,7 +279,7 @@ class AddCustomToken extends PureComponent {
280279

281280
validateCustomTokenAddress = async (address) => {
282281
let validated = true;
283-
const isValidTokenAddress = isValidAddress(address);
282+
const isValidTokenAddress = isValidHexAddress(address);
284283

285284
const { chainId } = this.props;
286285
const toSmartContract =

app/components/UI/ApprovalTagUrl/ApprovalTagUrl.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { toChecksumAddress } from 'ethereumjs-util';
1+
22
import React, { useEffect, useMemo, useState } from 'react';
33
import { useSelector } from 'react-redux';
44

@@ -12,6 +12,7 @@ import { getHost, prefixUrlWithProtocol } from '../../../util/browser';
1212
import useFavicon from '../../hooks/useFavicon/useFavicon';
1313
import stylesheet from './ApprovalTagUrl.styles';
1414
import { INTERNAL_ORIGINS } from '../../../constants/transaction';
15+
import { safeToChecksumAddress } from '../../../util/address';
1516

1617
const { ORIGIN_DEEPLINK, ORIGIN_QR_CODE } = AppConstants.DEEPLINKS;
1718
export const APPROVAL_TAG_URL_ORIGIN_PILL = 'APPROVAL_TAG_URL_ORIGIN_PILL';
@@ -40,7 +41,7 @@ const ApprovalTagUrl = ({
4041
const accountsByChainId = useSelector(selectAccountsByChainId);
4142

4243
const internalAccounts = useSelector(selectInternalAccounts);
43-
const activeAddress = toChecksumAddress(from);
44+
const activeAddress = safeToChecksumAddress(from);
4445

4546
useEffect(() => {
4647
const isOriginDeepLinkVal =

app/components/UI/PaymentRequest/AssetList/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { fontStyles } from '../../../../styles/common';
66
import Identicon from '../../Identicon';
77
import NetworkMainAssetLogo from '../../NetworkMainAssetLogo';
88
import { useSelector } from 'react-redux';
9-
import { toChecksumAddress } from 'ethereumjs-util';
109
import { useTheme } from '../../../../util/theme';
1110
import { selectTokenList } from '../../../../selectors/tokenListController';
1211
import { ImportTokenViewSelectorsIDs } from '../../../../../e2e/selectors/wallet/ImportTokenView.selectors';
12+
import { safeToChecksumAddress } from '../../../../util/address';
1313

1414
// TODO: Replace "any" with type
1515
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -99,9 +99,10 @@ const AssetList = ({
9999
if (isETH) {
100100
return <NetworkMainAssetLogo big style={styles.ethLogo} />;
101101
}
102+
const checksumAddress = address ? safeToChecksumAddress(address) : undefined;
102103
const token =
103-
tokenList?.[toChecksumAddress(address)] ||
104-
tokenList?.[address.toLowerCase()];
104+
(checksumAddress && tokenList?.[checksumAddress]) ||
105+
(address && tokenList?.[address.toLowerCase()]);
105106
const iconUrl = token?.iconUrl;
106107
if (!iconUrl) {
107108
return <Identicon address={address} />;

app/components/UI/Ramp/hooks/useBalance.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export default function useBalance(asset?: Asset) {
9797
balance =
9898
assetAddress && assetAddress in balances
9999
? renderFromTokenMinimalUnit(
100-
balances[assetAddress],
100+
balances[assetAddress as Hex],
101101
asset.decimals ?? 18,
102102
)
103103
: 0;
@@ -109,7 +109,7 @@ export default function useBalance(asset?: Asset) {
109109
);
110110
balanceBN =
111111
assetAddress && assetAddress in balances
112-
? hexToBN(balances[assetAddress])
112+
? hexToBN(balances[assetAddress as Hex])
113113
: null;
114114
}
115115

app/components/UI/Swaps/components/TokenSelectModal.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import Icon from 'react-native-vector-icons/Ionicons';
1616
import FAIcon from 'react-native-vector-icons/FontAwesome5';
1717
import Fuse from 'fuse.js';
1818
import { connect } from 'react-redux';
19-
import { isValidAddress } from 'ethereumjs-util';
2019

2120
import Device from '../../../../util/device';
2221
import { addCurrencySymbol } from '../../../../util/number';
@@ -52,6 +51,7 @@ import { useTheme } from '../../../../util/theme';
5251
import { QuoteViewSelectorIDs } from '../../../../../e2e/selectors/swaps/QuoteView.selectors';
5352
import { getDecimalChainId } from '../../../../util/networks';
5453
import { getSortedTokensByFiatValue } from '../utils/token-list-utils';
54+
import { isValidHexAddress } from '../../../../util/address';
5555

5656
const createStyles = (colors) =>
5757
StyleSheet.create({
@@ -230,7 +230,7 @@ function TokenSelectModal({
230230
const shouldFetchToken = useMemo(
231231
() =>
232232
tokenSearchResults.length === 0 &&
233-
isValidAddress(searchString) &&
233+
isValidHexAddress(searchString) &&
234234
!excludedAddresses.includes(searchString?.toLowerCase()),
235235
[excludedAddresses, searchString, tokenSearchResults.length],
236236
);

app/components/UI/Swaps/utils/useFiatConversionRates.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export function useFiatConversionRates({
5858
return fetchTokenContractExchangeRates({
5959
tokenPricesService: new CodefiTokenPricesServiceV2(),
6060
nativeCurrency: currentCurrency,
61-
tokenAddresses: [checksumAddress],
61+
tokenAddresses: [checksumAddress as Hex],
6262
chainId,
6363
});
6464
}, [

app/components/UI/TransactionElement/utils.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@ import { sumHexWEIs } from '../../../util/conversions';
2222
import {
2323
decodeTransferData,
2424
isCollectibleAddress,
25-
getTicker,
2625
getActionKey,
2726
TRANSACTION_TYPES,
2827
calculateEIP1559GasFeeHexes,
2928
} from '../../../util/transactions';
30-
import { toChecksumAddress } from 'ethereumjs-util';
3129
import { swapsUtils } from '@metamask/swaps-controller';
3230
import { isSwapsNativeAsset } from '../Swaps/utils';
3331
import { toLowerCaseEquals } from '../../../util/general';
@@ -303,7 +301,7 @@ export function decodeIncomingTransfer(args) {
303301
: undefined;
304302
const exchangeRate =
305303
token && contractExchangeRates
306-
? contractExchangeRates[toChecksumAddress(token.address)]?.price
304+
? contractExchangeRates[safeToChecksumAddress(token.address)]?.price
307305
: undefined;
308306

309307
let renderTokenFiatAmount, renderTokenFiatNumber;

app/components/Views/QRScanner/index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
'use strict';
55
import { useNavigation } from '@react-navigation/native';
66
import { parse } from 'eth-url-parser';
7-
import { isValidAddress } from 'ethereumjs-util';
87
import React, { useCallback, useRef } from 'react';
98
import { Alert, Image, InteractionManager, View, Linking } from 'react-native';
109
import Text, {
@@ -20,7 +19,7 @@ import AppConstants from '../../../core/AppConstants';
2019
import SharedDeeplinkManager from '../../../core/DeeplinkManager/SharedDeeplinkManager';
2120
import Engine from '../../../core/Engine';
2221
import { selectChainId } from '../../../selectors/networkController';
23-
import { isValidAddressInputViaQRCode } from '../../../util/address';
22+
import { isValidAddressInputViaQRCode, isValidHexAddress } from '../../../util/address';
2423
import { getURLProtocol } from '../../../util/general';
2524
import {
2625
failedSeedPhraseRequirements,
@@ -183,7 +182,7 @@ const QRScanner = ({
183182
if (
184183
(content.split(`${PROTOCOLS.ETHEREUM}:`).length > 1 &&
185184
!parse(content).function_name) ||
186-
(content.startsWith('0x') && isValidAddress(content))
185+
(content.startsWith('0x') && isValidHexAddress(content))
187186
) {
188187
const handledContent = content.startsWith('0x')
189188
? `${PROTOCOLS.ETHEREUM}:${content}@${currentChainId}`

app/components/Views/Settings/Contacts/ContactForm/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import PropTypes from 'prop-types';
1313
import { getEditableOptions } from '../../../../UI/Navbar';
1414
import StyledButton from '../../../../UI/StyledButton';
1515
import Engine from '../../../../../core/Engine';
16-
import { toChecksumAddress } from 'ethereumjs-util';
1716
import { connect } from 'react-redux';
1817
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
1918
import { strings } from '../../../../../../locales/i18n';
2019
import {
2120
renderShortAddress,
21+
safeToChecksumAddress,
2222
validateAddressOrENS,
2323
} from '../../../../../util/address';
2424
import ErrorMessage from '../../../confirmations/legacy/SendFlow/ErrorMessage';
@@ -272,7 +272,7 @@ class ContactForm extends PureComponent {
272272
const { AddressBookController } = Engine.context;
273273
if (!name || !address) return;
274274
AddressBookController.set(
275-
toChecksumAddress(toEnsAddress || address),
275+
safeToChecksumAddress(toEnsAddress || address),
276276
name,
277277
chainId,
278278
memo,

app/components/Views/confirmations/components/Confirm/Info/TypedSignV3V4/InfoSectionOriginAndDetails/InfoSectionOriginAndDetails.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { useSignatureRequest } from '../../../../../hooks/useSignatureRequest';
1313
import useApprovalRequest from '../../../../../hooks/useApprovalRequest';
1414
import { View } from 'react-native';
1515
import styleSheet from './InfoSectionOriginAndDetails.styles';
16-
import { isValidAddress } from 'ethereumjs-util';
16+
import { isValidHexAddress } from '../../../../../../../../util/address';
17+
1718

1819
export const InfoSectionOriginAndDetails = () => {
1920
const { styles } = useStyles(styleSheet, {});
@@ -56,7 +57,7 @@ export const InfoSectionOriginAndDetails = () => {
5657
>
5758
<DisplayURL url={origin} />
5859
</InfoRow>
59-
{isValidAddress(verifyingContract) && (
60+
{isValidHexAddress(verifyingContract) && (
6061
<InfoRow label={strings('confirm.label.interacting_with')}>
6162
<InfoRowAddress
6263
address={verifyingContract}

app/components/Views/confirmations/hooks/useAccountInfo.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
import { toChecksumAddress } from 'ethereumjs-util';
21
import { useMemo } from 'react';
32
import { useSelector } from 'react-redux';
43

54
import Engine from '../../../../core/Engine';
65
import useAddressBalance from '../../../../components/hooks/useAddressBalance/useAddressBalance';
76
import { selectInternalAccounts } from '../../../../selectors/accountsController';
8-
import { renderAccountName } from '../../../../util/address';
7+
import {
8+
renderAccountName,
9+
safeToChecksumAddress,
10+
} from '../../../../util/address';
911
import { selectCurrentCurrency } from '../../../../selectors/currencyRateController';
1012
import { formatWithThreshold } from '../../../../util/assets';
1113
import I18n from '../../../../../locales/i18n';
1214

1315
const useAccountInfo = (address: string) => {
1416
const internalAccounts = useSelector(selectInternalAccounts);
15-
const activeAddress = toChecksumAddress(address);
17+
const activeAddress = safeToChecksumAddress(address);
1618
const { addressBalance: accountBalance } = useAddressBalance(
1719
undefined,
1820
address,

0 commit comments

Comments
 (0)