From 3519fb3311e02dab0fbfd98093e123860840d393 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Thu, 13 May 2021 16:08:42 -0500 Subject: [PATCH 1/6] create safer isValidAddress method --- app/scripts/controllers/preferences.js | 4 +- .../controllers/transactions/lib/util.js | 9 ++- app/scripts/lib/typed-message-manager.js | 4 +- shared/modules/hexstring-utils.js | 31 ++++++++ shared/modules/hexstring-utils.test.js | 51 ++++++++++++ .../ui/identicon/identicon.component.js | 13 ++-- .../ui/identicon/identicon.component.test.js | 6 +- ui/helpers/utils/icon-factory.js | 9 ++- ui/helpers/utils/util.js | 34 -------- ui/helpers/utils/util.test.js | 78 +------------------ ui/pages/add-token/add-token.component.js | 8 +- .../add-recipient/add-recipient.component.js | 7 +- .../add-recipient/add-recipient.js | 10 ++- .../add-recipient/add-recipient.utils.test.js | 8 +- .../add-recipient/ens-input.component.js | 23 +++--- ui/pages/send/send.component.js | 10 ++- .../add-contact/add-contact.component.js | 11 +-- .../edit-contact/edit-contact.component.js | 10 ++- ui/pages/settings/settings.container.js | 4 +- ui/pages/swaps/swaps.util.js | 16 ++-- 20 files changed, 179 insertions(+), 167 deletions(-) create mode 100644 shared/modules/hexstring-utils.js create mode 100644 shared/modules/hexstring-utils.test.js diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index c20dcc6a0dc3..bebed6576d19 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -2,12 +2,12 @@ import { strict as assert } from 'assert'; import { ObservableStore } from '@metamask/obs-store'; import { ethErrors } from 'eth-rpc-errors'; import { normalize as normalizeAddress } from 'eth-sig-util'; -import { isValidAddress } from 'ethereumjs-util'; import ethers from 'ethers'; import log from 'loglevel'; import { LISTED_CONTRACT_ADDRESSES } from '../../../shared/constants/tokens'; import { NETWORK_TYPE_TO_ID_MAP } from '../../../shared/constants/network'; import { isPrefixedFormattedHexString } from '../../../shared/modules/network.utils'; +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; import { NETWORK_EVENTS } from './network'; export default class PreferencesController { @@ -867,7 +867,7 @@ export default class PreferencesController { `Invalid decimals "${decimals}": must be 0 <= 36.`, ); } - if (!isValidAddress(address)) { + if (!isValidHexAddress(address, false)) { throw ethErrors.rpc.invalidParams(`Invalid address "${address}".`); } } diff --git a/app/scripts/controllers/transactions/lib/util.js b/app/scripts/controllers/transactions/lib/util.js index 70652a3c1dbb..ac5686b30c14 100644 --- a/app/scripts/controllers/transactions/lib/util.js +++ b/app/scripts/controllers/transactions/lib/util.js @@ -1,7 +1,7 @@ -import { isValidAddress } from 'ethereumjs-util'; import { ethErrors } from 'eth-rpc-errors'; import { addHexPrefix } from '../../../lib/util'; import { TRANSACTION_STATUSES } from '../../../../../shared/constants/transaction'; +import { isValidHexAddress } from '../../../../../shared/modules/hexstring-utils'; const normalizers = { from: (from) => addHexPrefix(from), @@ -110,7 +110,7 @@ export function validateFrom(txParams) { `Invalid "from" address "${txParams.from}": not a string.`, ); } - if (!isValidAddress(txParams.from)) { + if (!isValidHexAddress(txParams.from)) { throw ethErrors.rpc.invalidParams('Invalid "from" address.'); } } @@ -128,7 +128,10 @@ export function validateRecipient(txParams) { } else { throw ethErrors.rpc.invalidParams('Invalid "to" address.'); } - } else if (txParams.to !== undefined && !isValidAddress(txParams.to)) { + } else if ( + txParams.to !== undefined && + !isValidHexAddress(txParams.to, false) + ) { throw ethErrors.rpc.invalidParams('Invalid "to" address.'); } return txParams; diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 3da405cf1b7e..57abb79b944b 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -3,12 +3,12 @@ import { strict as assert } from 'assert'; import { ObservableStore } from '@metamask/obs-store'; import { ethErrors } from 'eth-rpc-errors'; import { typedSignatureHash, TYPED_MESSAGE_SCHEMA } from 'eth-sig-util'; -import { isValidAddress } from 'ethereumjs-util'; import log from 'loglevel'; import jsonschema from 'jsonschema'; import { MESSAGE_TYPE } from '../../../shared/constants/app'; import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller'; import createId from '../../../shared/modules/random-id'; +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; /** * Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a @@ -160,7 +160,7 @@ export default class TypedMessageManager extends EventEmitter { assert.ok('data' in params, 'Params must include a "data" field.'); assert.ok('from' in params, 'Params must include a "from" field.'); assert.ok( - typeof params.from === 'string' && isValidAddress(params.from), + typeof params.from === 'string' && isValidHexAddress(params.from, false), '"from" field must be a valid, lowercase, hexadecimal Ethereum address string.', ); diff --git a/shared/modules/hexstring-utils.js b/shared/modules/hexstring-utils.js new file mode 100644 index 000000000000..c4a52b0686e9 --- /dev/null +++ b/shared/modules/hexstring-utils.js @@ -0,0 +1,31 @@ +import { + isHexString, + isValidAddress, + isValidChecksumAddress, + addHexPrefix, +} from 'ethereumjs-util'; + +export const BURN_ADDRESS = '0x0000000000000000000000000000000000000000'; + +export function isBurnAddress(address) { + return address === BURN_ADDRESS; +} + +export function isValidHexAddress(possibleAddress, allowNonPrefixed = true) { + const addressToCheck = allowNonPrefixed + ? addHexPrefix(possibleAddress) + : possibleAddress; + if (!isHexString(addressToCheck)) { + return false; + } + + const prefixRemoved = addressToCheck.slice(2); + const lower = prefixRemoved.toLowerCase(); + const upper = prefixRemoved.toUpperCase(); + const allOneCase = prefixRemoved === lower || prefixRemoved === upper; + if (!allOneCase) { + return isValidChecksumAddress(addressToCheck); + } + + return isValidAddress(addressToCheck); +} diff --git a/shared/modules/hexstring-utils.test.js b/shared/modules/hexstring-utils.test.js new file mode 100644 index 000000000000..0e174c809b05 --- /dev/null +++ b/shared/modules/hexstring-utils.test.js @@ -0,0 +1,51 @@ +import { strict as assert } from 'assert'; +import { toChecksumAddress } from 'ethereumjs-util'; +import { isValidHexAddress } from './hexstring-utils'; + +describe('hexstring utils', function () { + describe('isValidHexAddress', function () { + it('should allow 40-char non-prefixed hex', function () { + const address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b825'; + const result = isValidHexAddress(address); + assert.equal(result, true); + }); + + it('should allow 42-char prefixed hex', function () { + const address = '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825'; + const result = isValidHexAddress(address); + assert.equal(result, true); + }); + + it('should NOT allow any length of non hex-prefixed string', function () { + const address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b85'; + const result = isValidHexAddress(address); + assert.equal(result, false); + }); + + it('should NOT allow less than 42 character hex-prefixed string', function () { + const address = '0xfdea65ce26263f6d9a1b5de9555d2931a33b85'; + const result = isValidHexAddress(address); + assert.equal(result, false); + }); + + it('should recognize correct capitalized checksum', function () { + const address = '0xFDEa65C8e26263F6d9A1B5de9555D2931A33b825'; + const result = isValidHexAddress(address); + assert.equal(result, true); + }); + + it('should recognize incorrect capitalized checksum', function () { + const address = '0xFDea65C8e26263F6d9A1B5de9555D2931A33b825'; + const result = isValidHexAddress(address); + assert.equal(result, false); + }); + + it('should recognize this sample hashed address', function () { + const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9Bb0'; + const result = isValidHexAddress(address); + const hashed = toChecksumAddress(address.toLowerCase()); + assert.equal(hashed, address); + assert.equal(result, true); + }); + }); +}); diff --git a/ui/components/ui/identicon/identicon.component.js b/ui/components/ui/identicon/identicon.component.js index d52717d05004..6adf89858a32 100644 --- a/ui/components/ui/identicon/identicon.component.js +++ b/ui/components/ui/identicon/identicon.component.js @@ -3,7 +3,8 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import contractMap from '@metamask/contract-metadata'; -import { checksumAddress, isHex } from '../../../helpers/utils/util'; +import { isValidHexAddress } from '../../../../shared/modules/hexstring-utils'; +import { checksumAddress } from '../../../helpers/utils/util'; import Jazzicon from '../jazzicon'; import BlockieIdenticon from './blockieIdenticon'; @@ -84,13 +85,11 @@ export default class Identicon extends PureComponent { return this.renderImage(); } - if (address) { - if (isHex(address)) { - const checksummedAddress = checksumAddress(address); + if (isValidHexAddress(address)) { + const checksummedAddress = checksumAddress(address); - if (contractMap[checksummedAddress]?.logo) { - return this.renderJazzicon(); - } + if (contractMap[checksummedAddress]?.logo) { + return this.renderJazzicon(); } return ( diff --git a/ui/components/ui/identicon/identicon.component.test.js b/ui/components/ui/identicon/identicon.component.test.js index f78a47b6ded6..fb9702fe85f3 100644 --- a/ui/components/ui/identicon/identicon.component.test.js +++ b/ui/components/ui/identicon/identicon.component.test.js @@ -38,7 +38,11 @@ describe('Identicon', () => { it('renders div with address prop', () => { const wrapper = mount( - , + , ); expect(wrapper.find('div.test-address').prop('className')).toStrictEqual( diff --git a/ui/helpers/utils/icon-factory.js b/ui/helpers/utils/icon-factory.js index 4d69b842f66c..2aa60f5e34e0 100644 --- a/ui/helpers/utils/icon-factory.js +++ b/ui/helpers/utils/icon-factory.js @@ -1,5 +1,6 @@ import contractMap from '@metamask/contract-metadata'; -import { isValidAddress, checksumAddress, isHex } from './util'; +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; +import { checksumAddress } from './util'; let iconFactory; @@ -18,7 +19,7 @@ function IconFactory(jazzicon) { IconFactory.prototype.iconForAddress = function (address, diameter) { let addr = address; - if (isHex(address)) { + if (isValidHexAddress(address, false)) { addr = checksumAddress(address); } @@ -52,7 +53,9 @@ IconFactory.prototype.generateNewIdenticon = function (address, diameter) { function iconExistsFor(address) { return ( - contractMap[address] && isValidAddress(address) && contractMap[address].logo + contractMap[address] && + isValidHexAddress(address) && + contractMap[address].logo ); } diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index f52e824d8f4d..42cfe605cb19 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -78,20 +78,6 @@ export function addressSummary( : '...'; } -export function isValidAddress(address) { - if (!address || address === '0x0000000000000000000000000000000000000000') { - return false; - } - const prefixed = addHexPrefix(address); - if (!isHex(prefixed)) { - return false; - } - return ( - (isAllOneCase(prefixed.slice(2)) && ethUtil.isValidAddress(prefixed)) || - ethUtil.isValidChecksumAddress(prefixed) - ); -} - export function isValidDomainName(address) { const match = punycode .toASCII(address) @@ -112,15 +98,6 @@ export function isOriginContractAddress(to, sendTokenAddress) { return to.toLowerCase() === sendTokenAddress.toLowerCase(); } -export function isAllOneCase(address) { - if (!address) { - return true; - } - const lower = address.toLowerCase(); - const upper = address.toUpperCase(); - return address === lower || address === upper; -} - // Takes wei Hex, returns wei BN, even if input is null export function numericBalance(balance) { if (!balance) { @@ -182,10 +159,6 @@ export function formatBalance( return formatted; } -export function isHex(str) { - return Boolean(str.match(/^(0x)?[0-9a-fA-F]+$/u)); -} - export function getContractAtAddress(tokenAddress) { return global.eth.contract(abi).at(tokenAddress); } @@ -253,13 +226,6 @@ export function shortenAddress(address = '') { return `${address.slice(0, 6)}...${address.slice(-4)}`; } -export function isValidAddressHead(address) { - const addressLengthIsLessThanFull = address.length < 42; - const addressIsHex = isHex(address); - - return addressLengthIsLessThanFull && addressIsHex; -} - export function getAccountByAddress(accounts = [], targetAddress) { return accounts.find(({ address }) => address === targetAddress); } diff --git a/ui/helpers/utils/util.test.js b/ui/helpers/utils/util.test.js index 0dfa431f6836..7038dd79e1f9 100644 --- a/ui/helpers/utils/util.test.js +++ b/ui/helpers/utils/util.test.js @@ -1,4 +1,4 @@ -import { BN, toChecksumAddress } from 'ethereumjs-util'; +import { BN } from 'ethereumjs-util'; import * as util from './util'; describe('util', () => { @@ -47,52 +47,6 @@ describe('util', () => { }); }); - describe('#isValidAddress', () => { - it('should allow 40-char non-prefixed hex', () => { - const address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b825'; - const result = util.isValidAddress(address); - expect(result).toStrictEqual(true); - }); - - it('should allow 42-char non-prefixed hex', () => { - const address = '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825'; - const result = util.isValidAddress(address); - expect(result).toStrictEqual(true); - }); - - it('should not allow less non hex-prefixed', () => { - const address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b85'; - const result = util.isValidAddress(address); - expect(result).toStrictEqual(false); - }); - - it('should not allow less hex-prefixed', () => { - const address = '0xfdea65ce26263f6d9a1b5de9555d2931a33b85'; - const result = util.isValidAddress(address); - expect(result).toStrictEqual(false); - }); - - it('should recognize correct capitalized checksum', () => { - const address = '0xFDEa65C8e26263F6d9A1B5de9555D2931A33b825'; - const result = util.isValidAddress(address); - expect(result).toStrictEqual(true); - }); - - it('should recognize incorrect capitalized checksum', () => { - const address = '0xFDea65C8e26263F6d9A1B5de9555D2931A33b825'; - const result = util.isValidAddress(address); - expect(result).toStrictEqual(false); - }); - - it('should recognize this sample hashed address', () => { - const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9Bb0'; - const result = util.isValidAddress(address); - const hashed = toChecksumAddress(address.toLowerCase()); - expect(hashed).toStrictEqual(address); - expect(result).toStrictEqual(true); - }); - }); - describe('isValidDomainName', () => { it('should return true when given a valid domain name', () => { expect(util.isValidDomainName('foo.bar')).toStrictEqual(true); @@ -239,36 +193,6 @@ describe('util', () => { }); describe('normalizing values', function () { - describe('#isHex', function () { - it('should return true when given a hex string', function () { - const result = util.isHex( - 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2', - ); - expect(result).toStrictEqual(true); - }); - - it('should return false when given a non-hex string', () => { - const result = util.isHex( - 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714imnotreal', - ); - expect(result).toStrictEqual(false); - }); - - it('should return false when given a string containing a non letter/number character', () => { - const result = util.isHex( - 'c3ab8ff13720!8ad9047dd39466b3c%8974e592c2fa383d4a396071imnotreal', - ); - expect(result).toStrictEqual(false); - }); - - it('should return true when given a hex string with hex-prefix', () => { - const result = util.isHex( - '0xc3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2', - ); - expect(result).toStrictEqual(true); - }); - }); - describe('#getRandomFileName', () => { it('should only return a string containing alphanumeric characters', () => { const result = util.getRandomFileName(); diff --git a/ui/pages/add-token/add-token.component.js b/ui/pages/add-token/add-token.component.js index 1aad2e2c8d50..b5c6bac27034 100644 --- a/ui/pages/add-token/add-token.component.js +++ b/ui/pages/add-token/add-token.component.js @@ -1,15 +1,13 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { - checkExistingAddresses, - isValidAddress, -} from '../../helpers/utils/util'; +import { checkExistingAddresses } from '../../helpers/utils/util'; import { tokenInfoGetter } from '../../helpers/utils/token-util'; import { CONFIRM_ADD_TOKEN_ROUTE } from '../../helpers/constants/routes'; import TextField from '../../components/ui/text-field'; import PageContainer from '../../components/ui/page-container'; import { Tabs, Tab } from '../../components/ui/tabs'; import { addHexPrefix } from '../../../app/scripts/lib/util'; +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; import TokenList from './token-list'; import TokenSearch from './token-search'; @@ -167,7 +165,7 @@ class AddToken extends Component { autoFilled: false, }); - const addressIsValid = isValidAddress(customAddress); + const addressIsValid = isValidHexAddress(customAddress, false); const standardAddress = addHexPrefix(customAddress).toLowerCase(); switch (true) { diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.component.js b/ui/pages/send/send-content/add-recipient/add-recipient.component.js index 82ccfa1d4f5d..d2308f4df967 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.component.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.component.js @@ -2,13 +2,16 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Fuse from 'fuse.js'; import Identicon from '../../../../components/ui/identicon'; -import { isValidAddress } from '../../../../helpers/utils/util'; import Dialog from '../../../../components/ui/dialog'; import ContactList from '../../../../components/app/contact-list'; import RecipientGroup from '../../../../components/app/contact-list/recipient-group/recipient-group.component'; import { ellipsify } from '../../send.utils'; import Button from '../../../../components/ui/button'; import Confusable from '../../../../components/ui/confusable'; +import { + isBurnAddress, + isValidHexAddress, +} from '../../../../../shared/modules/hexstring-utils'; export default class AddRecipient extends Component { static propTypes = { @@ -101,7 +104,7 @@ export default class AddRecipient extends Component { let content; - if (isValidAddress(query)) { + if (!isBurnAddress(query) && isValidHexAddress(query)) { content = this.renderExplicitAddress(query); } else if (ensResolution) { content = this.renderExplicitAddress( diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.js b/ui/pages/send/send-content/add-recipient/add-recipient.js index 3ba0077c7ab5..49f681b00108 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.js @@ -11,18 +11,24 @@ import { } from '../../send.constants'; import { - isValidAddress, checkExistingAddresses, isValidDomainName, isOriginContractAddress, isDefaultMetaMaskChain, } from '../../../../helpers/utils/util'; +import { + isBurnAddress, + isValidHexAddress, +} from '../../../../../shared/modules/hexstring-utils'; export function getToErrorObject(to, sendTokenAddress, chainId) { let toError = null; if (!to) { toError = REQUIRED_ERROR; - } else if (!isValidAddress(to) && !isValidDomainName(to)) { + } else if ( + isBurnAddress(to) || + (!isValidHexAddress(to) && !isValidDomainName(to)) + ) { toError = isDefaultMetaMaskChain(chainId) ? INVALID_RECIPIENT_ADDRESS_ERROR : INVALID_RECIPIENT_ADDRESS_NOT_ETH_NETWORK_ERROR; diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.utils.test.js b/ui/pages/send/send-content/add-recipient/add-recipient.utils.test.js index 954fc6ab8c7e..9e57f781bd02 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.utils.test.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.utils.test.js @@ -11,13 +11,19 @@ jest.mock('../../../../helpers/utils/util', () => ({ isDefaultMetaMaskChain: jest.fn().mockReturnValue(true), isEthNetwork: jest.fn().mockReturnValue(true), checkExistingAddresses: jest.fn().mockReturnValue(true), - isValidAddress: jest.fn((to) => Boolean(to.match(/^[0xabcdef123456798]+$/u))), isValidDomainName: jest.requireActual('../../../../helpers/utils/util') .isValidDomainName, isOriginContractAddress: jest.requireActual('../../../../helpers/utils/util') .isOriginContractAddress, })); +jest.mock('../../../../../shared/modules/hexstring-utils', () => ({ + isValidHexAddress: jest.fn((to) => + Boolean(to.match(/^[0xabcdef123456798]+$/u)), + ), + isBurnAddress: jest.fn(() => false), +})); + describe('add-recipient utils', () => { describe('getToErrorObject()', () => { it('should return a required error if "to" is falsy', () => { diff --git a/ui/pages/send/send-content/add-recipient/ens-input.component.js b/ui/pages/send/send-content/add-recipient/ens-input.component.js index 9d55d13a7974..0b6661248d98 100644 --- a/ui/pages/send/send-content/add-recipient/ens-input.component.js +++ b/ui/pages/send/send-content/add-recipient/ens-input.component.js @@ -7,13 +7,14 @@ import copyToClipboard from 'copy-to-clipboard/index'; import ENS from 'ethjs-ens'; import networkMap from 'ethereum-ens-network-map'; import log from 'loglevel'; +import { isHexString } from 'ethereumjs-util'; import { ellipsify } from '../../send.utils'; -import { - isValidDomainName, - isValidAddress, - isValidAddressHead, -} from '../../../../helpers/utils/util'; +import { isValidDomainName } from '../../../../helpers/utils/util'; import { MAINNET_NETWORK_ID } from '../../../../../shared/constants/network'; +import { + isBurnAddress, + isValidHexAddress, +} from '../../../../../shared/modules/hexstring-utils'; // Local Constants const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; @@ -143,7 +144,7 @@ export default class EnsInput extends Component { onPaste = (event) => { event.clipboardData.items[0].getAsString((text) => { - if (isValidAddress(text)) { + if (!isBurnAddress(text) && isValidHexAddress(text)) { this.props.onPaste(text); } }); @@ -170,8 +171,8 @@ export default class EnsInput extends Component { if ( !networkHasEnsSupport && - !isValidAddress(input) && - !isValidAddressHead(input) + !(isBurnAddress(input) === false && isValidHexAddress(input)) && + !isHexString(input) ) { updateEnsResolution(''); updateEnsResolutionError( @@ -182,7 +183,11 @@ export default class EnsInput extends Component { if (isValidDomainName(input)) { this.lookupEnsName(input); - } else if (onValidAddressTyped && isValidAddress(input)) { + } else if ( + onValidAddressTyped && + !isBurnAddress(input) && + isValidHexAddress(input) + ) { onValidAddressTyped(input); } else { updateEnsResolution(''); diff --git a/ui/pages/send/send.component.js b/ui/pages/send/send.component.js index 56eafb1e7e7e..cb3b312efcae 100644 --- a/ui/pages/send/send.component.js +++ b/ui/pages/send/send.component.js @@ -1,7 +1,10 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { debounce } from 'lodash'; -import { isValidAddress } from '../../helpers/utils/util'; +import { + isBurnAddress, + isValidHexAddress, +} from '../../../shared/modules/hexstring-utils'; import { getAmountErrorObject, getGasFeeErrorObject, @@ -171,7 +174,10 @@ export default class SendTransactionScreen extends Component { if (qrCodeData) { if (qrCodeData.type === 'address') { scannedAddress = qrCodeData.values.address.toLowerCase(); - if (isValidAddress(scannedAddress)) { + if ( + !isBurnAddress(scannedAddress) && + isValidHexAddress(scannedAddress) + ) { const currentAddress = prevTo?.toLowerCase(); if (currentAddress !== scannedAddress) { updateSendTo(scannedAddress); diff --git a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js index 51640edf5090..833223a8de26 100644 --- a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js +++ b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js @@ -4,12 +4,13 @@ import { debounce } from 'lodash'; import Identicon from '../../../../components/ui/identicon'; import TextField from '../../../../components/ui/text-field'; import { CONTACT_LIST_ROUTE } from '../../../../helpers/constants/routes'; -import { - isValidAddress, - isValidDomainName, -} from '../../../../helpers/utils/util'; +import { isValidDomainName } from '../../../../helpers/utils/util'; import EnsInput from '../../../send/send-content/add-recipient/ens-input'; import PageContainerFooter from '../../../../components/ui/page-container/page-container-footer'; +import { + isBurnAddress, + isValidHexAddress, +} from '../../../../../shared/modules/hexstring-utils'; export default class AddContact extends PureComponent { static contextTypes = { @@ -53,7 +54,7 @@ export default class AddContact extends PureComponent { } validate = (address) => { - const valid = isValidAddress(address); + const valid = !isBurnAddress(address) && isValidHexAddress(address); const validEnsAddress = isValidDomainName(address); if (valid || validEnsAddress || address === '') { diff --git a/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js b/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js index 0ccc1f64afbd..a5744e851909 100644 --- a/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js +++ b/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js @@ -4,8 +4,11 @@ import { Redirect } from 'react-router-dom'; import Identicon from '../../../../components/ui/identicon'; import Button from '../../../../components/ui/button/button.component'; import TextField from '../../../../components/ui/text-field'; -import { isValidAddress } from '../../../../helpers/utils/util'; import PageContainerFooter from '../../../../components/ui/page-container/page-container-footer'; +import { + isBurnAddress, + isValidHexAddress, +} from '../../../../../shared/modules/hexstring-utils'; export default class EditContact extends PureComponent { static contextTypes = { @@ -135,7 +138,10 @@ export default class EditContact extends PureComponent { this.state.newAddress !== address ) { // if the user makes a valid change to the address field, remove the original address - if (isValidAddress(this.state.newAddress)) { + if ( + !isBurnAddress(this.state.newAddress) && + isValidHexAddress(this.state.newAddress) + ) { await removeFromAddressBook(chainId, address); await addToAddressBook( this.state.newAddress, diff --git a/ui/pages/settings/settings.container.js b/ui/pages/settings/settings.container.js index a9aeb9d6f07d..a13fb871466c 100644 --- a/ui/pages/settings/settings.container.js +++ b/ui/pages/settings/settings.container.js @@ -2,10 +2,10 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { getAddressBookEntryName } from '../../selectors'; -import { isValidAddress, isHex } from '../../helpers/utils/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../shared/constants/app'; import { getEnvironmentType } from '../../../app/scripts/lib/util'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; import { ABOUT_US_ROUTE, @@ -64,7 +64,7 @@ const mapStateToProps = (state, ownProps) => { const addressName = getAddressBookEntryName( state, - isHex(pathNameTail) && isValidAddress(pathNameTail) ? pathNameTail : '', + isValidHexAddress(pathNameTail) ? pathNameTail : '', ); return { diff --git a/ui/pages/swaps/swaps.util.js b/ui/pages/swaps/swaps.util.js index 45f200a2f35a..592416ac23e7 100644 --- a/ui/pages/swaps/swaps.util.js +++ b/ui/pages/swaps/swaps.util.js @@ -1,7 +1,6 @@ import log from 'loglevel'; import BigNumber from 'bignumber.js'; import abi from 'human-standard-token-abi'; -import { isValidAddress } from 'ethereumjs-util'; import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP, METASWAP_CHAINID_API_HOST_MAP, @@ -35,6 +34,7 @@ import { formatCurrency } from '../../helpers/utils/confirm-tx.util'; import fetchWithCache from '../../helpers/utils/fetch-with-cache'; import { calcGasTotal } from '../send/send.utils'; +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; const TOKEN_TRANSFER_LOG_TOPIC_HASH = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; @@ -74,8 +74,8 @@ const QUOTE_VALIDATORS = [ validator: (trade) => trade && validHex(trade.data) && - isValidAddress(trade.to) && - isValidAddress(trade.from) && + isValidHexAddress(trade.to) && + isValidHexAddress(trade.from) && truthyString(trade.value), }, { @@ -85,8 +85,8 @@ const QUOTE_VALIDATORS = [ approvalTx === null || (approvalTx && validHex(approvalTx.data) && - isValidAddress(approvalTx.to) && - isValidAddress(approvalTx.from)), + isValidHexAddress(approvalTx.to) && + isValidHexAddress(approvalTx.from)), }, { property: 'sourceAmount', @@ -101,12 +101,12 @@ const QUOTE_VALIDATORS = [ { property: 'sourceToken', type: 'string', - validator: isValidAddress, + validator: isValidHexAddress, }, { property: 'destinationToken', type: 'string', - validator: isValidAddress, + validator: isValidHexAddress, }, { property: 'aggregator', @@ -146,7 +146,7 @@ const TOKEN_VALIDATORS = [ { property: 'address', type: 'string', - validator: isValidAddress, + validator: isValidHexAddress, }, { property: 'symbol', From f0140d6a0dc9fcab3db60e6b59f68045d1e2bea0 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Mon, 17 May 2021 09:12:39 -0500 Subject: [PATCH 2/6] feedback --- app/scripts/controllers/preferences.js | 2 +- app/scripts/lib/typed-message-manager.js | 3 ++- shared/modules/hexstring-utils.js | 19 ++++++++++++++++++- shared/modules/hexstring-utils.test.js | 6 ++++++ ui/helpers/utils/icon-factory.js | 2 +- ui/pages/add-token/add-token.component.js | 4 +++- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index bebed6576d19..19aef453ede8 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -867,7 +867,7 @@ export default class PreferencesController { `Invalid decimals "${decimals}": must be 0 <= 36.`, ); } - if (!isValidHexAddress(address, false)) { + if (!isValidHexAddress(address, { allowNonPrefixed: false })) { throw ethErrors.rpc.invalidParams(`Invalid address "${address}".`); } } diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 57abb79b944b..8e205520b2a4 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -160,7 +160,8 @@ export default class TypedMessageManager extends EventEmitter { assert.ok('data' in params, 'Params must include a "data" field.'); assert.ok('from' in params, 'Params must include a "from" field.'); assert.ok( - typeof params.from === 'string' && isValidHexAddress(params.from, false), + typeof params.from === 'string' && + isValidHexAddress(params.from, { allowNonPrefixed: false }), '"from" field must be a valid, lowercase, hexadecimal Ethereum address string.', ); diff --git a/shared/modules/hexstring-utils.js b/shared/modules/hexstring-utils.js index c4a52b0686e9..0b335d824051 100644 --- a/shared/modules/hexstring-utils.js +++ b/shared/modules/hexstring-utils.js @@ -11,7 +11,24 @@ export function isBurnAddress(address) { return address === BURN_ADDRESS; } -export function isValidHexAddress(possibleAddress, allowNonPrefixed = true) { +/** + * Validates that the input is a hex address. This utility method is a thin + * wrapper around ethereumjs-util.isValidAddress, with the exception that it + * does not throw an error when provided values that are not hex strings. In + * addition, and by default, this method will return true for hex strings that + * meet the length requirement of a hex address, but are not prefixed with `0x` + * Finally, if a mixed case string is provided this method assumes the address + * is a checksum address and will validate it as such. + * @param {string} possibleAddress - Input parameter to check against + * @param {Object} [options] - options bag + * @param {boolean} [options.allowNonPrefixed] - If true will first ensure '0x' is + * prepended to the string + * @returns {boolean} whether or not the input is a valid hex address + */ +export function isValidHexAddress( + possibleAddress, + { allowNonPrefixed = true } = {}, +) { const addressToCheck = allowNonPrefixed ? addHexPrefix(possibleAddress) : possibleAddress; diff --git a/shared/modules/hexstring-utils.test.js b/shared/modules/hexstring-utils.test.js index 0e174c809b05..14bd4208632a 100644 --- a/shared/modules/hexstring-utils.test.js +++ b/shared/modules/hexstring-utils.test.js @@ -16,6 +16,12 @@ describe('hexstring utils', function () { assert.equal(result, true); }); + it('should NOT allow 40-char non-prefixed hex when allowNonPrefixed is false', function () { + const address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b825'; + const result = isValidHexAddress(address, { allowNonPrefixed: false }); + assert.equal(result, false); + }); + it('should NOT allow any length of non hex-prefixed string', function () { const address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b85'; const result = isValidHexAddress(address); diff --git a/ui/helpers/utils/icon-factory.js b/ui/helpers/utils/icon-factory.js index 2aa60f5e34e0..2995d9fe1e58 100644 --- a/ui/helpers/utils/icon-factory.js +++ b/ui/helpers/utils/icon-factory.js @@ -19,7 +19,7 @@ function IconFactory(jazzicon) { IconFactory.prototype.iconForAddress = function (address, diameter) { let addr = address; - if (isValidHexAddress(address, false)) { + if (isValidHexAddress(address, { allowNonPrefixed: false })) { addr = checksumAddress(address); } diff --git a/ui/pages/add-token/add-token.component.js b/ui/pages/add-token/add-token.component.js index b5c6bac27034..8a6c77f149b7 100644 --- a/ui/pages/add-token/add-token.component.js +++ b/ui/pages/add-token/add-token.component.js @@ -165,7 +165,9 @@ class AddToken extends Component { autoFilled: false, }); - const addressIsValid = isValidHexAddress(customAddress, false); + const addressIsValid = isValidHexAddress(customAddress, { + allowNonPrefixed: false, + }); const standardAddress = addHexPrefix(customAddress).toLowerCase(); switch (true) { From 12b3631f617db73cf016a2b278435dafb1e66af6 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Mon, 17 May 2021 11:23:14 -0500 Subject: [PATCH 3/6] add new opt in checksum address validation --- .../controllers/transactions/lib/util.js | 4 +-- shared/modules/hexstring-utils.js | 27 +++++++++++-------- shared/modules/hexstring-utils.test.js | 6 ++--- ui/helpers/utils/icon-factory.js | 4 +-- .../add-recipient/add-recipient.component.js | 5 +++- .../add-recipient/add-recipient.js | 3 ++- .../add-recipient/ens-input.component.js | 12 ++++++--- ui/pages/send/send.component.js | 10 ++----- .../add-contact/add-contact.component.js | 4 ++- .../edit-contact/edit-contact.component.js | 4 ++- ui/pages/settings/settings.container.js | 10 +++++-- ui/pages/swaps/swaps.util.js | 8 +++--- 12 files changed, 58 insertions(+), 39 deletions(-) diff --git a/app/scripts/controllers/transactions/lib/util.js b/app/scripts/controllers/transactions/lib/util.js index ac5686b30c14..804f43ec2dc4 100644 --- a/app/scripts/controllers/transactions/lib/util.js +++ b/app/scripts/controllers/transactions/lib/util.js @@ -110,7 +110,7 @@ export function validateFrom(txParams) { `Invalid "from" address "${txParams.from}": not a string.`, ); } - if (!isValidHexAddress(txParams.from)) { + if (!isValidHexAddress(txParams.from, { allowNonPrefixed: false })) { throw ethErrors.rpc.invalidParams('Invalid "from" address.'); } } @@ -130,7 +130,7 @@ export function validateRecipient(txParams) { } } else if ( txParams.to !== undefined && - !isValidHexAddress(txParams.to, false) + !isValidHexAddress(txParams.to, { allowNonPrefixed: false }) ) { throw ethErrors.rpc.invalidParams('Invalid "to" address.'); } diff --git a/shared/modules/hexstring-utils.js b/shared/modules/hexstring-utils.js index 0b335d824051..f40f537d3248 100644 --- a/shared/modules/hexstring-utils.js +++ b/shared/modules/hexstring-utils.js @@ -17,17 +17,20 @@ export function isBurnAddress(address) { * does not throw an error when provided values that are not hex strings. In * addition, and by default, this method will return true for hex strings that * meet the length requirement of a hex address, but are not prefixed with `0x` - * Finally, if a mixed case string is provided this method assumes the address - * is a checksum address and will validate it as such. + * Finally, if the mixedCaseUseChecksum flag is true and a mixed case string is + * provided this method will validate it has the proper checksum formatting. * @param {string} possibleAddress - Input parameter to check against * @param {Object} [options] - options bag - * @param {boolean} [options.allowNonPrefixed] - If true will first ensure '0x' is - * prepended to the string + * @param {boolean} [options.allowNonPrefixed] - If true will first ensure '0x' + * is prepended to the string + * @param {boolean} [options.mixedCaseUseChecksum] - If true will treat mixed + * case addresses as checksum addresses and validate that proper checksum + * format is used * @returns {boolean} whether or not the input is a valid hex address */ export function isValidHexAddress( possibleAddress, - { allowNonPrefixed = true } = {}, + { allowNonPrefixed = true, mixedCaseUseChecksum = false } = {}, ) { const addressToCheck = allowNonPrefixed ? addHexPrefix(possibleAddress) @@ -36,12 +39,14 @@ export function isValidHexAddress( return false; } - const prefixRemoved = addressToCheck.slice(2); - const lower = prefixRemoved.toLowerCase(); - const upper = prefixRemoved.toUpperCase(); - const allOneCase = prefixRemoved === lower || prefixRemoved === upper; - if (!allOneCase) { - return isValidChecksumAddress(addressToCheck); + if (mixedCaseUseChecksum) { + const prefixRemoved = addressToCheck.slice(2); + const lower = prefixRemoved.toLowerCase(); + const upper = prefixRemoved.toUpperCase(); + const allOneCase = prefixRemoved === lower || prefixRemoved === upper; + if (!allOneCase) { + return isValidChecksumAddress(addressToCheck); + } } return isValidAddress(addressToCheck); diff --git a/shared/modules/hexstring-utils.test.js b/shared/modules/hexstring-utils.test.js index 14bd4208632a..3d292451e3c1 100644 --- a/shared/modules/hexstring-utils.test.js +++ b/shared/modules/hexstring-utils.test.js @@ -36,19 +36,19 @@ describe('hexstring utils', function () { it('should recognize correct capitalized checksum', function () { const address = '0xFDEa65C8e26263F6d9A1B5de9555D2931A33b825'; - const result = isValidHexAddress(address); + const result = isValidHexAddress(address, { mixedCaseUseChecksum: true }); assert.equal(result, true); }); it('should recognize incorrect capitalized checksum', function () { const address = '0xFDea65C8e26263F6d9A1B5de9555D2931A33b825'; - const result = isValidHexAddress(address); + const result = isValidHexAddress(address, { mixedCaseUseChecksum: true }); assert.equal(result, false); }); it('should recognize this sample hashed address', function () { const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9Bb0'; - const result = isValidHexAddress(address); + const result = isValidHexAddress(address, { mixedCaseUseChecksum: true }); const hashed = toChecksumAddress(address.toLowerCase()); assert.equal(hashed, address); assert.equal(result, true); diff --git a/ui/helpers/utils/icon-factory.js b/ui/helpers/utils/icon-factory.js index 2995d9fe1e58..cfda16423d91 100644 --- a/ui/helpers/utils/icon-factory.js +++ b/ui/helpers/utils/icon-factory.js @@ -19,7 +19,7 @@ function IconFactory(jazzicon) { IconFactory.prototype.iconForAddress = function (address, diameter) { let addr = address; - if (isValidHexAddress(address, { allowNonPrefixed: false })) { + if (isValidHexAddress(address)) { addr = checksumAddress(address); } @@ -54,7 +54,7 @@ IconFactory.prototype.generateNewIdenticon = function (address, diameter) { function iconExistsFor(address) { return ( contractMap[address] && - isValidHexAddress(address) && + isValidHexAddress(address, { allowNonPrefixed: false }) && contractMap[address].logo ); } diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.component.js b/ui/pages/send/send-content/add-recipient/add-recipient.component.js index d2308f4df967..da7999c94d76 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.component.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.component.js @@ -104,7 +104,10 @@ export default class AddRecipient extends Component { let content; - if (!isBurnAddress(query) && isValidHexAddress(query)) { + if ( + !isBurnAddress(query) && + isValidHexAddress(query, { mixedCaseUseChecksum: true }) + ) { content = this.renderExplicitAddress(query); } else if (ensResolution) { content = this.renderExplicitAddress( diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.js b/ui/pages/send/send-content/add-recipient/add-recipient.js index 49f681b00108..c1fee499b403 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.js @@ -27,7 +27,8 @@ export function getToErrorObject(to, sendTokenAddress, chainId) { toError = REQUIRED_ERROR; } else if ( isBurnAddress(to) || - (!isValidHexAddress(to) && !isValidDomainName(to)) + (!isValidHexAddress(to, { mixedCaseUseChecksum: true }) && + !isValidDomainName(to)) ) { toError = isDefaultMetaMaskChain(chainId) ? INVALID_RECIPIENT_ADDRESS_ERROR diff --git a/ui/pages/send/send-content/add-recipient/ens-input.component.js b/ui/pages/send/send-content/add-recipient/ens-input.component.js index 0b6661248d98..658ac9bde6c0 100644 --- a/ui/pages/send/send-content/add-recipient/ens-input.component.js +++ b/ui/pages/send/send-content/add-recipient/ens-input.component.js @@ -144,7 +144,10 @@ export default class EnsInput extends Component { onPaste = (event) => { event.clipboardData.items[0].getAsString((text) => { - if (!isBurnAddress(text) && isValidHexAddress(text)) { + if ( + !isBurnAddress(text) && + isValidHexAddress(text, { mixedCaseUseChecksum: true }) + ) { this.props.onPaste(text); } }); @@ -171,7 +174,10 @@ export default class EnsInput extends Component { if ( !networkHasEnsSupport && - !(isBurnAddress(input) === false && isValidHexAddress(input)) && + !( + isBurnAddress(input) === false && + isValidHexAddress(input, { mixedCaseUseChecksum: true }) + ) && !isHexString(input) ) { updateEnsResolution(''); @@ -186,7 +192,7 @@ export default class EnsInput extends Component { } else if ( onValidAddressTyped && !isBurnAddress(input) && - isValidHexAddress(input) + isValidHexAddress(input, { mixedCaseUseChecksum: true }) ) { onValidAddressTyped(input); } else { diff --git a/ui/pages/send/send.component.js b/ui/pages/send/send.component.js index cb3b312efcae..ca489ee59437 100644 --- a/ui/pages/send/send.component.js +++ b/ui/pages/send/send.component.js @@ -1,10 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { debounce } from 'lodash'; -import { - isBurnAddress, - isValidHexAddress, -} from '../../../shared/modules/hexstring-utils'; +import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; import { getAmountErrorObject, getGasFeeErrorObject, @@ -174,10 +171,7 @@ export default class SendTransactionScreen extends Component { if (qrCodeData) { if (qrCodeData.type === 'address') { scannedAddress = qrCodeData.values.address.toLowerCase(); - if ( - !isBurnAddress(scannedAddress) && - isValidHexAddress(scannedAddress) - ) { + if (isValidHexAddress(scannedAddress)) { const currentAddress = prevTo?.toLowerCase(); if (currentAddress !== scannedAddress) { updateSendTo(scannedAddress); diff --git a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js index 833223a8de26..e454838d121b 100644 --- a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js +++ b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js @@ -54,7 +54,9 @@ export default class AddContact extends PureComponent { } validate = (address) => { - const valid = !isBurnAddress(address) && isValidHexAddress(address); + const valid = + !isBurnAddress(address) && + isValidHexAddress(address, { mixedCaseUseChecksum: true }); const validEnsAddress = isValidDomainName(address); if (valid || validEnsAddress || address === '') { diff --git a/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js b/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js index a5744e851909..51419b302a23 100644 --- a/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js +++ b/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js @@ -140,7 +140,9 @@ export default class EditContact extends PureComponent { // if the user makes a valid change to the address field, remove the original address if ( !isBurnAddress(this.state.newAddress) && - isValidHexAddress(this.state.newAddress) + isValidHexAddress(this.state.newAddress, { + mixedCaseUseChecksum: true, + }) ) { await removeFromAddressBook(chainId, address); await addToAddressBook( diff --git a/ui/pages/settings/settings.container.js b/ui/pages/settings/settings.container.js index a13fb871466c..bf428c4fc65c 100644 --- a/ui/pages/settings/settings.container.js +++ b/ui/pages/settings/settings.container.js @@ -5,7 +5,10 @@ import { getAddressBookEntryName } from '../../selectors'; import { ENVIRONMENT_TYPE_POPUP } from '../../../shared/constants/app'; import { getEnvironmentType } from '../../../app/scripts/lib/util'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; -import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; +import { + isValidHexAddress, + isBurnAddress, +} from '../../../shared/modules/hexstring-utils'; import { ABOUT_US_ROUTE, @@ -64,7 +67,10 @@ const mapStateToProps = (state, ownProps) => { const addressName = getAddressBookEntryName( state, - isValidHexAddress(pathNameTail) ? pathNameTail : '', + !isBurnAddress(pathNameTail) && + isValidHexAddress(pathNameTail, { mixedCaseUseChecksum: true }) + ? pathNameTail + : '', ); return { diff --git a/ui/pages/swaps/swaps.util.js b/ui/pages/swaps/swaps.util.js index 592416ac23e7..777df3f6a2ec 100644 --- a/ui/pages/swaps/swaps.util.js +++ b/ui/pages/swaps/swaps.util.js @@ -74,8 +74,8 @@ const QUOTE_VALIDATORS = [ validator: (trade) => trade && validHex(trade.data) && - isValidHexAddress(trade.to) && - isValidHexAddress(trade.from) && + isValidHexAddress(trade.to, { allowNonPrefixed: false }) && + isValidHexAddress(trade.from, { allowNonPrefixed: false }) && truthyString(trade.value), }, { @@ -85,8 +85,8 @@ const QUOTE_VALIDATORS = [ approvalTx === null || (approvalTx && validHex(approvalTx.data) && - isValidHexAddress(approvalTx.to) && - isValidHexAddress(approvalTx.from)), + isValidHexAddress(approvalTx.to, { allowNonPrefixed: false }) && + isValidHexAddress(approvalTx.from, { allowNonPrefixed: false })), }, { property: 'sourceAmount', From 8dcd521d271d24bb21f873d70d5511d45bc12c0f Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Mon, 17 May 2021 12:11:16 -0500 Subject: [PATCH 4/6] fixing last discrepancies --- ui/pages/send/send.component.js | 2 +- ui/pages/swaps/swaps.util.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/pages/send/send.component.js b/ui/pages/send/send.component.js index ca489ee59437..6954f9cfca07 100644 --- a/ui/pages/send/send.component.js +++ b/ui/pages/send/send.component.js @@ -171,7 +171,7 @@ export default class SendTransactionScreen extends Component { if (qrCodeData) { if (qrCodeData.type === 'address') { scannedAddress = qrCodeData.values.address.toLowerCase(); - if (isValidHexAddress(scannedAddress)) { + if (isValidHexAddress(scannedAddress, { allowNonPrefixed: false })) { const currentAddress = prevTo?.toLowerCase(); if (currentAddress !== scannedAddress) { updateSendTo(scannedAddress); diff --git a/ui/pages/swaps/swaps.util.js b/ui/pages/swaps/swaps.util.js index 777df3f6a2ec..e72483f350c8 100644 --- a/ui/pages/swaps/swaps.util.js +++ b/ui/pages/swaps/swaps.util.js @@ -101,12 +101,12 @@ const QUOTE_VALIDATORS = [ { property: 'sourceToken', type: 'string', - validator: isValidHexAddress, + validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }), }, { property: 'destinationToken', type: 'string', - validator: isValidHexAddress, + validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }), }, { property: 'aggregator', @@ -146,7 +146,7 @@ const TOKEN_VALIDATORS = [ { property: 'address', type: 'string', - validator: isValidHexAddress, + validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }), }, { property: 'symbol', From d568288793f1363a073fa4523a64b4060174ec28 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Mon, 17 May 2021 13:12:32 -0500 Subject: [PATCH 5/6] do not use isValidHexAddress for jazzicon test --- ui/components/ui/identicon/identicon.component.js | 6 +++--- ui/helpers/utils/icon-factory.js | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/components/ui/identicon/identicon.component.js b/ui/components/ui/identicon/identicon.component.js index 6adf89858a32..6086d241e4fa 100644 --- a/ui/components/ui/identicon/identicon.component.js +++ b/ui/components/ui/identicon/identicon.component.js @@ -2,8 +2,8 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import contractMap from '@metamask/contract-metadata'; +import { isHexString, addHexPrefix } from 'ethereumjs-util'; -import { isValidHexAddress } from '../../../../shared/modules/hexstring-utils'; import { checksumAddress } from '../../../helpers/utils/util'; import Jazzicon from '../jazzicon'; import BlockieIdenticon from './blockieIdenticon'; @@ -85,8 +85,8 @@ export default class Identicon extends PureComponent { return this.renderImage(); } - if (isValidHexAddress(address)) { - const checksummedAddress = checksumAddress(address); + if (isHexString(addHexPrefix(address))) { + const checksummedAddress = checksumAddress(addHexPrefix(address)); if (contractMap[checksummedAddress]?.logo) { return this.renderJazzicon(); diff --git a/ui/helpers/utils/icon-factory.js b/ui/helpers/utils/icon-factory.js index cfda16423d91..030302a309d5 100644 --- a/ui/helpers/utils/icon-factory.js +++ b/ui/helpers/utils/icon-factory.js @@ -1,4 +1,5 @@ import contractMap from '@metamask/contract-metadata'; +import { isHexString, addHexPrefix } from 'ethereumjs-util'; import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; import { checksumAddress } from './util'; @@ -19,8 +20,8 @@ function IconFactory(jazzicon) { IconFactory.prototype.iconForAddress = function (address, diameter) { let addr = address; - if (isValidHexAddress(address)) { - addr = checksumAddress(address); + if (isHexString(addHexPrefix(address))) { + addr = checksumAddress(addHexPrefix(address)); } if (iconExistsFor(addr)) { From 63861cbd2e260007af330e5a827fd9fde47f472b Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Mon, 17 May 2021 13:38:12 -0500 Subject: [PATCH 6/6] restore address check --- ui/components/ui/identicon/identicon.component.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/components/ui/identicon/identicon.component.js b/ui/components/ui/identicon/identicon.component.js index 6086d241e4fa..ec2cc70cc7b4 100644 --- a/ui/components/ui/identicon/identicon.component.js +++ b/ui/components/ui/identicon/identicon.component.js @@ -85,8 +85,10 @@ export default class Identicon extends PureComponent { return this.renderImage(); } - if (isHexString(addHexPrefix(address))) { - const checksummedAddress = checksumAddress(addHexPrefix(address)); + if (address) { + const checksummedAddress = + isHexString(addHexPrefix(address)) && + checksumAddress(addHexPrefix(address)); if (contractMap[checksummedAddress]?.logo) { return this.renderJazzicon();