Skip to content

Commit

Permalink
Merge branch '1.0' into issue/2731
Browse files Browse the repository at this point in the history
  • Loading branch information
nivida committed Apr 26, 2019
2 parents 88bd5dc + aa5286e commit fc1950a
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 38 deletions.
51 changes: 48 additions & 3 deletions docs/_build/html/_sources/web3-utils.rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ isAddress

.. code-block:: javascript
web3.utils.isAddress(address)
web3.utils.isAddress(address [, chainId])
Checks if a given string is a valid Ethereum address.
It will also check the checksum, if the address has upper and lowercase letters.
Expand All @@ -418,6 +418,7 @@ Parameters
----------

1. ``address`` - ``String``: An address string.
2. ``chainId`` - ``number`` (optional): Chain id where checksummed address should be valid, defaults to ``null``. RSKIP-60 <https://github.com/rsksmart/RSKIPs/blob/master/IPs/RSKIP60.md> for details.

-------
Returns
Expand Down Expand Up @@ -446,6 +447,9 @@ Example
web3.utils.isAddress('0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d');
> false // wrong checksum
web3.utils.isAddress('0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD', 30);
> true
------------------------------------------------------------------------------


Expand All @@ -454,7 +458,7 @@ toChecksumAddress

.. code-block:: javascript
web3.utils.toChecksumAddress(address)
web3.utils.toChecksumAddress(address [, chainId])
Will convert an upper or lowercase Ethereum address to a checksum address.

Expand All @@ -463,6 +467,7 @@ Parameters
----------

1. ``address`` - ``String``: An address string.
2. ``chainId`` - ``number`` (optional): Chain id where checksummed address should be valid, defaults to ``null``. RSKIP-60 <https://github.com/rsksmart/RSKIPs/blob/master/IPs/RSKIP60.md> for details.

-------
Returns
Expand All @@ -482,6 +487,42 @@ Example
web3.utils.toChecksumAddress('0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D');
> "0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d" // same as above
web3.utils.toChecksumAddress('0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed', 30);
> "0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD"
------------------------------------------------------------------------------


stripHexPrefix
=====================

.. code-block:: javascript
web3.utils.stripHexPrefix(address)
Removes prefix from address if exists.

----------
Parameters
----------

1. ``address`` - ``String``: An address string.

-------
Returns
-------

``String``: address without prefix.

-------
Example
-------

.. code-block:: javascript
web3.utils.stripHexPrefix('0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d');
> "c1912fEE45d61C87Cc5EA59DaE31190FFFFf232d"
------------------------------------------------------------------------------

Expand All @@ -491,7 +532,7 @@ checkAddressChecksum

.. code-block:: javascript
web3.utils.checkAddressChecksum(address)
web3.utils.checkAddressChecksum(address [, chainId])
Checks the checksum of a given address. Will also return false on non-checksum addresses.

Expand All @@ -500,6 +541,7 @@ Parameters
----------

1. ``address`` - ``String``: An address string.
2. ``chainId`` - ``number`` (optional): Chain id where checksummed address should be valid, defaults to ``null``. RSKIP-60 <https://github.com/rsksmart/RSKIPs/blob/master/IPs/RSKIP60.md> for details.

-------
Returns
Expand All @@ -516,6 +558,9 @@ Example
web3.utils.checkAddressChecksum('0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d');
> true
web3.utils.checkAddressChecksum('0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd', 31);
> true
------------------------------------------------------------------------------

Expand Down
42 changes: 29 additions & 13 deletions packages/web3-utils/src/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ export const toTwosComplement = (number) => {
*
* @param {String} address the given HEX address
*
* @param {Number} chainId to define checksum behavior
*
* @returns {Boolean}
*/
export const isAddress = (address) => {
export const isAddress = (address, chainId = null) => {
// check if it has the basic requirements of an address
if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
return false;
Expand All @@ -107,30 +109,44 @@ export const isAddress = (address) => {
return true;
// Otherwise check each case
} else {
return checkAddressChecksum(address);
return checkAddressChecksum(address, chainId);
}
};

/**
* Removes prefix from address if exists.
*
* @method stripHexPrefix
*
* @param {string} address
*
* @returns {string} address without prefix
*/
export const stripHexPrefix = (str) => {
return str.slice(0, 2) === '0x' ? str.slice(2) : str
}

/**
* Checks if the given string is a checksummed address
*
* @method checkAddressChecksum
*
* @param {String} address the given HEX address
*
* @param {number} chain where checksummed address should be valid.
*
* @returns {Boolean}
*/
export const checkAddressChecksum = (address) => {
// Check each case
address = address.replace(/^0x/i, '');
const addressHash = keccak256(address.toLowerCase()).replace(/^0x/i, '');

for (let i = 0; i < 40; i++) {
// the nth letter should be uppercase if the nth digit of casemap is 1
if (
(parseInt(addressHash[i], 16) > 7 && address[i].toUpperCase() !== address[i]) ||
(parseInt(addressHash[i], 16) <= 7 && address[i].toLowerCase() !== address[i])
) {
export const checkAddressChecksum = (address, chainId = null) => {
const stripAddress = stripHexPrefix(address).toLowerCase()
const prefix = chainId != null ? (chainId.toString() + '0x') : ''
const keccakHash = Hash.keccak256(prefix + stripAddress).toString('hex').replace(/^0x/i, '');

for (let i = 0; i < stripAddress.length; i++) {
let output = parseInt(keccakHash[i], 16) >= 8 ?
stripAddress[i].toUpperCase() :
stripAddress[i];
if (stripHexPrefix(address)[i] !== output) {
return false;
}
}
Expand Down
33 changes: 18 additions & 15 deletions packages/web3-utils/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import isString from 'lodash/isString';
import isArray from 'lodash/isArray';
import * as utils from './Utils';
import * as ethjsUnit from 'ethjs-unit';
import Hash from 'eth-lib/lib/hash';

export BN from 'bn.js';
export {soliditySha3} from './SoliditySha3';
Expand Down Expand Up @@ -240,31 +241,32 @@ export const toWei = (number, unit) => {
*
* @method toChecksumAddress
*
* @param {String} address the given HEX address
* @param {string} address the given HEX address
*
* @returns {String}
* @param {number} chain where checksummed address should be valid.
*
* @returns {string} address with checksum applied.
*/
export const toChecksumAddress = (address) => {
if (typeof address === 'undefined') return '';
export const toChecksumAddress = (address, chainId = null) => {
if (typeof address !== 'string') {
return '';
}

if (!/^(0x)?[0-9a-f]{40}$/i.test(address))
throw new Error(`Given address "${address}" is not a valid Ethereum address.`);

address = address.toLowerCase().replace(/^0x/i, '');
const addressHash = utils.keccak256(address).replace(/^0x/i, '');
const stripAddress = stripHexPrefix(address).toLowerCase();
const prefix = chainId != null ? (chainId.toString() + '0x') : '';
const keccakHash = Hash.keccak256(prefix + stripAddress).toString('hex').replace(/^0x/i, '');
let checksumAddress = '0x';

for (let i = 0; i < address.length; i++) {
// If ith character is 9 to f then make it uppercase
if (parseInt(addressHash[i], 16) > 7) {
checksumAddress += address[i].toUpperCase();
} else {
checksumAddress += address[i];
}
}
for (let i = 0; i < stripAddress.length; i++)
checksumAddress += parseInt(keccakHash[i], 16) >= 8 ?
stripAddress[i].toUpperCase() :
stripAddress[i];

return checksumAddress;
};
}

// aliases
export const keccak256 = utils.keccak256;
Expand Down Expand Up @@ -297,3 +299,4 @@ export const isBloom = utils.isBloom;
export const isTopic = utils.isTopic;
export const bytesToHex = utils.bytesToHex;
export const hexToBytes = utils.hexToBytes;
export const stripHexPrefix = utils.stripHexPrefix;
75 changes: 74 additions & 1 deletion packages/web3-utils/tests/src/UtilsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
toUtf8,
toWei,
utf8ToHex,
getSignatureParameters
getSignatureParameters,
toChecksumAddress,
stripHexPrefix
} from '../../src';

/**
Expand Down Expand Up @@ -140,6 +142,22 @@ describe('UtilsTest', () => {
});
});

it('calls isAddress with chainId 30 and returns the expected results', () => {
const tests = [
{value: '0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD', is: true},
{value: '0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359', is: true},
{value: '0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB', is: true},
{value: '0xE247a45c287191d435A8a5D72A7C8dc030451E9F', is: false},
{value: '0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB', is: true},
{value: '0xe247a45c287191d435a8a5d72a7c8dc030451e9f', is: true},
{value: '0xE247A45C287191D435A8A5D72A7C8DC030451E9F', is: true}
];

tests.forEach((test) => {
expect(isAddress(test.value, 30)).toEqual(test.is);
});
});

it('calls isBN and returns the expected results', () => {
const tests = [
{
Expand Down Expand Up @@ -181,6 +199,61 @@ describe('UtilsTest', () => {
});
});

it('calls checkAddressChecksum with chainId 31 and returns the expected results', () => {
const tests = [
{value: '0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd', is: true},
{value: '0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359', is: true},
{value: '0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB', is: true},
{value: '0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB', is: true},
{value: '0XD1220A0CF47C7B9BE7A2E6BA89F429762E7B9ADB', is: false},
{value: '0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb', is: false},
{value: '0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB', is: false},
{value: '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', is: false}
];

tests.forEach((test) => {
expect(checkAddressChecksum(test.value, 31)).toEqual(test.is);
});
});

it('calls toChecksumAddress with chainId 30 and returns the expected results', () => {
const tests = [
{value: '0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed', is: '0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD'},
{value: '0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359', is: '0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359'},
{value: '0xdbf03b407c01e7cd3cbea99509d93f8dddc8c6fb', is: '0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB'},
{value: '0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb', is: '0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB'}
];

tests.forEach((test) => {
expect(toChecksumAddress(test.value, 30)).toEqual(test.is);
});
});

it('calls toChecksumAddress and returns the expected results', () => {
const tests = [
{value: '0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed', is: '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed'},
{value: '0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359', is: '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359'},
{value: '0xdbf03b407c01e7cd3cbea99509d93f8dddc8c6fb', is: '0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB'},
{value: '0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb', is: '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb'}
];

tests.forEach((test) => {
expect(toChecksumAddress(test.value)).toEqual(test.is);
});
});

it('calls stripHexPrefix and returns the expected results', () => {
const tests = [
{value: '0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed', is: '5aaeb6053f3e94c9b9a09f33669435e7ef1beaed'},
{value: '0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359', is: 'fb6916095ca1df60bb79ce92ce3ea74c37c5d359'},
{value: 'dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb', is: 'dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb'}
];

tests.forEach((test) => {
expect(stripHexPrefix(test.value)).toEqual(test.is);
});
});

/* eslint-disable jest/no-identical-title */
describe('calls keccak256', () => {
it('should return keccak256 with hex prefix', () => {
Expand Down
Loading

0 comments on commit fc1950a

Please sign in to comment.