mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
create safer isValidAddress method (#11089)
This commit is contained in:
parent
8f44383837
commit
9386e3cb03
@ -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, { allowNonPrefixed: false })) {
|
||||
throw ethErrors.rpc.invalidParams(`Invalid address "${address}".`);
|
||||
}
|
||||
}
|
||||
|
@ -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, { allowNonPrefixed: false })) {
|
||||
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, { allowNonPrefixed: false })
|
||||
) {
|
||||
throw ethErrors.rpc.invalidParams('Invalid "to" address.');
|
||||
}
|
||||
return txParams;
|
||||
|
@ -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,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' && isValidAddress(params.from),
|
||||
typeof params.from === 'string' &&
|
||||
isValidHexAddress(params.from, { allowNonPrefixed: false }),
|
||||
'"from" field must be a valid, lowercase, hexadecimal Ethereum address string.',
|
||||
);
|
||||
|
||||
|
53
shared/modules/hexstring-utils.js
Normal file
53
shared/modules/hexstring-utils.js
Normal file
@ -0,0 +1,53 @@
|
||||
import {
|
||||
isHexString,
|
||||
isValidAddress,
|
||||
isValidChecksumAddress,
|
||||
addHexPrefix,
|
||||
} from 'ethereumjs-util';
|
||||
|
||||
export const BURN_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
export function isBurnAddress(address) {
|
||||
return address === BURN_ADDRESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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.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, mixedCaseUseChecksum = false } = {},
|
||||
) {
|
||||
const addressToCheck = allowNonPrefixed
|
||||
? addHexPrefix(possibleAddress)
|
||||
: possibleAddress;
|
||||
if (!isHexString(addressToCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
57
shared/modules/hexstring-utils.test.js
Normal file
57
shared/modules/hexstring-utils.test.js
Normal file
@ -0,0 +1,57 @@
|
||||
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 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);
|
||||
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, { mixedCaseUseChecksum: true });
|
||||
assert.equal(result, true);
|
||||
});
|
||||
|
||||
it('should recognize incorrect capitalized checksum', function () {
|
||||
const address = '0xFDea65C8e26263F6d9A1B5de9555D2931A33b825';
|
||||
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, { mixedCaseUseChecksum: true });
|
||||
const hashed = toChecksumAddress(address.toLowerCase());
|
||||
assert.equal(hashed, address);
|
||||
assert.equal(result, true);
|
||||
});
|
||||
});
|
||||
});
|
@ -2,8 +2,9 @@ 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 { checksumAddress, isHex } from '../../../helpers/utils/util';
|
||||
import { checksumAddress } from '../../../helpers/utils/util';
|
||||
import Jazzicon from '../jazzicon';
|
||||
import BlockieIdenticon from './blockieIdenticon';
|
||||
|
||||
@ -85,12 +86,12 @@ export default class Identicon extends PureComponent {
|
||||
}
|
||||
|
||||
if (address) {
|
||||
if (isHex(address)) {
|
||||
const checksummedAddress = checksumAddress(address);
|
||||
const checksummedAddress =
|
||||
isHexString(addHexPrefix(address)) &&
|
||||
checksumAddress(addHexPrefix(address));
|
||||
|
||||
if (contractMap[checksummedAddress]?.logo) {
|
||||
return this.renderJazzicon();
|
||||
}
|
||||
if (contractMap[checksummedAddress]?.logo) {
|
||||
return this.renderJazzicon();
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -38,7 +38,11 @@ describe('Identicon', () => {
|
||||
|
||||
it('renders div with address prop', () => {
|
||||
const wrapper = mount(
|
||||
<Identicon store={store} className="test-address" address="0x0" />,
|
||||
<Identicon
|
||||
store={store}
|
||||
className="test-address"
|
||||
address="0x0000000000000000000000000000000000000000"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('div.test-address').prop('className')).toStrictEqual(
|
||||
|
@ -1,5 +1,7 @@
|
||||
import contractMap from '@metamask/contract-metadata';
|
||||
import { isValidAddress, checksumAddress, isHex } from './util';
|
||||
import { isHexString, addHexPrefix } from 'ethereumjs-util';
|
||||
import { isValidHexAddress } from '../../../shared/modules/hexstring-utils';
|
||||
import { checksumAddress } from './util';
|
||||
|
||||
let iconFactory;
|
||||
|
||||
@ -18,8 +20,8 @@ function IconFactory(jazzicon) {
|
||||
IconFactory.prototype.iconForAddress = function (address, diameter) {
|
||||
let addr = address;
|
||||
|
||||
if (isHex(address)) {
|
||||
addr = checksumAddress(address);
|
||||
if (isHexString(addHexPrefix(address))) {
|
||||
addr = checksumAddress(addHexPrefix(address));
|
||||
}
|
||||
|
||||
if (iconExistsFor(addr)) {
|
||||
@ -52,7 +54,9 @@ IconFactory.prototype.generateNewIdenticon = function (address, diameter) {
|
||||
|
||||
function iconExistsFor(address) {
|
||||
return (
|
||||
contractMap[address] && isValidAddress(address) && contractMap[address].logo
|
||||
contractMap[address] &&
|
||||
isValidHexAddress(address, { allowNonPrefixed: false }) &&
|
||||
contractMap[address].logo
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,9 @@ class AddToken extends Component {
|
||||
autoFilled: false,
|
||||
});
|
||||
|
||||
const addressIsValid = isValidAddress(customAddress);
|
||||
const addressIsValid = isValidHexAddress(customAddress, {
|
||||
allowNonPrefixed: false,
|
||||
});
|
||||
const standardAddress = addHexPrefix(customAddress).toLowerCase();
|
||||
|
||||
switch (true) {
|
||||
|
@ -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,10 @@ export default class AddRecipient extends Component {
|
||||
|
||||
let content;
|
||||
|
||||
if (isValidAddress(query)) {
|
||||
if (
|
||||
!isBurnAddress(query) &&
|
||||
isValidHexAddress(query, { mixedCaseUseChecksum: true })
|
||||
) {
|
||||
content = this.renderExplicitAddress(query);
|
||||
} else if (ensResolution) {
|
||||
content = this.renderExplicitAddress(
|
||||
|
@ -11,18 +11,25 @@ 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, { mixedCaseUseChecksum: true }) &&
|
||||
!isValidDomainName(to))
|
||||
) {
|
||||
toError = isDefaultMetaMaskChain(chainId)
|
||||
? INVALID_RECIPIENT_ADDRESS_ERROR
|
||||
: INVALID_RECIPIENT_ADDRESS_NOT_ETH_NETWORK_ERROR;
|
||||
|
@ -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', () => {
|
||||
|
@ -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,10 @@ export default class EnsInput extends Component {
|
||||
|
||||
onPaste = (event) => {
|
||||
event.clipboardData.items[0].getAsString((text) => {
|
||||
if (isValidAddress(text)) {
|
||||
if (
|
||||
!isBurnAddress(text) &&
|
||||
isValidHexAddress(text, { mixedCaseUseChecksum: true })
|
||||
) {
|
||||
this.props.onPaste(text);
|
||||
}
|
||||
});
|
||||
@ -170,8 +174,11 @@ export default class EnsInput extends Component {
|
||||
|
||||
if (
|
||||
!networkHasEnsSupport &&
|
||||
!isValidAddress(input) &&
|
||||
!isValidAddressHead(input)
|
||||
!(
|
||||
isBurnAddress(input) === false &&
|
||||
isValidHexAddress(input, { mixedCaseUseChecksum: true })
|
||||
) &&
|
||||
!isHexString(input)
|
||||
) {
|
||||
updateEnsResolution('');
|
||||
updateEnsResolutionError(
|
||||
@ -182,7 +189,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, { mixedCaseUseChecksum: true })
|
||||
) {
|
||||
onValidAddressTyped(input);
|
||||
} else {
|
||||
updateEnsResolution('');
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { debounce } from 'lodash';
|
||||
import { isValidAddress } from '../../helpers/utils/util';
|
||||
import { isValidHexAddress } from '../../../shared/modules/hexstring-utils';
|
||||
import {
|
||||
getAmountErrorObject,
|
||||
getGasFeeErrorObject,
|
||||
@ -171,7 +171,7 @@ export default class SendTransactionScreen extends Component {
|
||||
if (qrCodeData) {
|
||||
if (qrCodeData.type === 'address') {
|
||||
scannedAddress = qrCodeData.values.address.toLowerCase();
|
||||
if (isValidAddress(scannedAddress)) {
|
||||
if (isValidHexAddress(scannedAddress, { allowNonPrefixed: false })) {
|
||||
const currentAddress = prevTo?.toLowerCase();
|
||||
if (currentAddress !== scannedAddress) {
|
||||
updateSendTo(scannedAddress);
|
||||
|
@ -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,9 @@ export default class AddContact extends PureComponent {
|
||||
}
|
||||
|
||||
validate = (address) => {
|
||||
const valid = isValidAddress(address);
|
||||
const valid =
|
||||
!isBurnAddress(address) &&
|
||||
isValidHexAddress(address, { mixedCaseUseChecksum: true });
|
||||
const validEnsAddress = isValidDomainName(address);
|
||||
|
||||
if (valid || validEnsAddress || address === '') {
|
||||
|
@ -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,12 @@ 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, {
|
||||
mixedCaseUseChecksum: true,
|
||||
})
|
||||
) {
|
||||
await removeFromAddressBook(chainId, address);
|
||||
await addToAddressBook(
|
||||
this.state.newAddress,
|
||||
|
@ -2,10 +2,13 @@ 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,
|
||||
isBurnAddress,
|
||||
} from '../../../shared/modules/hexstring-utils';
|
||||
|
||||
import {
|
||||
ABOUT_US_ROUTE,
|
||||
@ -64,7 +67,10 @@ const mapStateToProps = (state, ownProps) => {
|
||||
|
||||
const addressName = getAddressBookEntryName(
|
||||
state,
|
||||
isHex(pathNameTail) && isValidAddress(pathNameTail) ? pathNameTail : '',
|
||||
!isBurnAddress(pathNameTail) &&
|
||||
isValidHexAddress(pathNameTail, { mixedCaseUseChecksum: true })
|
||||
? pathNameTail
|
||||
: '',
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -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, { allowNonPrefixed: false }) &&
|
||||
isValidHexAddress(trade.from, { allowNonPrefixed: false }) &&
|
||||
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, { allowNonPrefixed: false }) &&
|
||||
isValidHexAddress(approvalTx.from, { allowNonPrefixed: false })),
|
||||
},
|
||||
{
|
||||
property: 'sourceAmount',
|
||||
@ -101,12 +101,12 @@ const QUOTE_VALIDATORS = [
|
||||
{
|
||||
property: 'sourceToken',
|
||||
type: 'string',
|
||||
validator: isValidAddress,
|
||||
validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }),
|
||||
},
|
||||
{
|
||||
property: 'destinationToken',
|
||||
type: 'string',
|
||||
validator: isValidAddress,
|
||||
validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }),
|
||||
},
|
||||
{
|
||||
property: 'aggregator',
|
||||
@ -146,7 +146,7 @@ const TOKEN_VALIDATORS = [
|
||||
{
|
||||
property: 'address',
|
||||
type: 'string',
|
||||
validator: isValidAddress,
|
||||
validator: (input) => isValidHexAddress(input, { allowNonPrefixed: false }),
|
||||
},
|
||||
{
|
||||
property: 'symbol',
|
||||
|
Loading…
Reference in New Issue
Block a user