From 18660b6ad4ee6fbd64d0d8bc13541527fb8932e1 Mon Sep 17 00:00:00 2001 From: legobt <6wbvkn0j@anonaddy.me> Date: Tue, 11 Jul 2023 03:44:43 +0000 Subject: [PATCH 1/5] feat: break out address checksum encoding to erc55EncodeAddress function --- src/hex.ts | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/hex.ts b/src/hex.ts index cbd3bfdab..5022410d7 100644 --- a/src/hex.ts +++ b/src/hex.ts @@ -79,6 +79,26 @@ export function isValidHexAddress(possibleAddress: Hex) { ); } +/** + * Encodes a passed hex string as an ERC-55 mixed-case checksum address. + * Specification: https://eips.ethereum.org/EIPS/eip-55. + * + * @param address - The hex address to encode. + * @returns The address encoded according to ERC-55. + */ +export function erc55EncodeAddress(address: Hex) { + const unPrefixed = remove0x(address.toLowerCase()); + const unPrefixedHash = remove0x(bytesToHex(keccak256(unPrefixed))); + return `0x${unPrefixed + .split('') + .map((character, nibbleIndex) => + parseInt(unPrefixedHash[nibbleIndex] as string, 16) > 7 + ? character.toUpperCase() + : character, + ) + .join('')}`; +} + /** * Validate that the passed hex string is a valid ERC-55 mixed-case * checksum address. @@ -91,22 +111,7 @@ export function isValidChecksumAddress(possibleChecksum: Hex) { return false; } - const unPrefixed = remove0x(possibleChecksum); - const unPrefixedHash = remove0x( - bytesToHex(keccak256(unPrefixed.toLowerCase())), - ); - - for (let i = 0; i < unPrefixedHash.length; i++) { - const value = parseInt(unPrefixedHash[i] as string, 16); - if ( - (value > 7 && unPrefixed[i]?.toUpperCase() !== unPrefixed[i]) || - (value <= 7 && unPrefixed[i]?.toLowerCase() !== unPrefixed[i]) - ) { - return false; - } - } - - return true; + return erc55EncodeAddress(possibleChecksum) === possibleChecksum; } /** From 4314ad43310d5080f26bba879fb60f44f8c3b4c0 Mon Sep 17 00:00:00 2001 From: legobt <6wbvkn0j@anonaddy.me> Date: Tue, 11 Jul 2023 04:11:09 +0000 Subject: [PATCH 2/5] deps: ethereum-cryptography@2.0.0->2.1.0; dedupe @noble/hashes --- yarn.lock | 53 +++++++++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/yarn.lock b/yarn.lock index b9ee57cc4..1cdcb33d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1100,23 +1100,16 @@ __metadata: languageName: unknown linkType: soft -"@noble/curves@npm:1.0.0, @noble/curves@npm:~1.0.0": - version: 1.0.0 - resolution: "@noble/curves@npm:1.0.0" +"@noble/curves@npm:1.1.0, @noble/curves@npm:~1.1.0": + version: 1.1.0 + resolution: "@noble/curves@npm:1.1.0" dependencies: - "@noble/hashes": 1.3.0 - checksum: 6bcef44d626c640dc8961819d68dd67dffb907e3b973b7c27efe0ecdd9a5c6ce62c7b9e3dfc930c66605dced7f1ec0514d191c09a2ce98d6d52b66e3315ffa79 + "@noble/hashes": 1.3.1 + checksum: 2658cdd3f84f71079b4e3516c47559d22cf4b55c23ac8ee9d2b1f8e5b72916d9689e59820e0f9d9cb4a46a8423af5b56dc6bb7782405c88be06a015180508db5 languageName: node linkType: hard -"@noble/hashes@npm:1.3.0": - version: 1.3.0 - resolution: "@noble/hashes@npm:1.3.0" - checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e - languageName: node - linkType: hard - -"@noble/hashes@npm:^1.3.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0": +"@noble/hashes@npm:1.3.1, @noble/hashes@npm:^1.3.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.1": version: 1.3.1 resolution: "@noble/hashes@npm:1.3.1" checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 @@ -1219,24 +1212,24 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.3.0": - version: 1.3.0 - resolution: "@scure/bip32@npm:1.3.0" +"@scure/bip32@npm:1.3.1": + version: 1.3.1 + resolution: "@scure/bip32@npm:1.3.1" dependencies: - "@noble/curves": ~1.0.0 - "@noble/hashes": ~1.3.0 + "@noble/curves": ~1.1.0 + "@noble/hashes": ~1.3.1 "@scure/base": ~1.1.0 - checksum: 6eae997f9bdf41fe848134898960ac48e645fa10e63d579be965ca331afd0b7c1b8ebac170770d237ab4099dafc35e5a82995384510025ccf2abe669f85e8918 + checksum: 394d65f77a40651eba21a5096da0f4233c3b50d422864751d373fcf142eeedb94a1149f9ab1dbb078086dab2d0bc27e2b1afec8321bf22d4403c7df2fea5bfe2 languageName: node linkType: hard -"@scure/bip39@npm:1.2.0": - version: 1.2.0 - resolution: "@scure/bip39@npm:1.2.0" +"@scure/bip39@npm:1.2.1": + version: 1.2.1 + resolution: "@scure/bip39@npm:1.2.1" dependencies: "@noble/hashes": ~1.3.0 "@scure/base": ~1.1.0 - checksum: 980d761f53e63de04a9e4db840eb13bfb1bd1b664ecb04a71824c12c190f4972fd84146f3ed89b2a8e4c6bd2c17c15f8b592b7ac029e903323b0f9e2dae6916b + checksum: c5bd6f1328fdbeae2dcdd891825b1610225310e5e62a4942714db51066866e4f7bef242c7b06a1b9dcc8043a4a13412cf5c5df76d3b10aa9e36b82e9b6e3eeaa languageName: node linkType: hard @@ -3231,14 +3224,14 @@ __metadata: linkType: hard "ethereum-cryptography@npm:^2.0.0": - version: 2.0.0 - resolution: "ethereum-cryptography@npm:2.0.0" + version: 2.1.0 + resolution: "ethereum-cryptography@npm:2.1.0" dependencies: - "@noble/curves": 1.0.0 - "@noble/hashes": 1.3.0 - "@scure/bip32": 1.3.0 - "@scure/bip39": 1.2.0 - checksum: 958f8aab2d1b32aa759fb27a27877b3647410e8bb9aca7d65d1d477db4864cf7fc46b918eb52a1e246c25e98ee0a35a632c88b496aeaefa13469ee767a76c8db + "@noble/curves": 1.1.0 + "@noble/hashes": 1.3.1 + "@scure/bip32": 1.3.1 + "@scure/bip39": 1.2.1 + checksum: 47bd69103f0553e5c98e0645c295ca74e0da53a92b8d26237287f528521cd2aa13d5cd1e288c36e59ce885451199cef8e4de424a93c45bacf54a06bdd09946a4 languageName: node linkType: hard From e150cc796aff5df581dde8d8d76f5f764da1c3d0 Mon Sep 17 00:00:00 2001 From: legobeat <109787230+legobeat@users.noreply.github.com> Date: Tue, 11 Jul 2023 17:36:11 +0900 Subject: [PATCH 3/5] Update jsdoc Co-authored-by: Maarten Zuidhoorn --- src/hex.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hex.ts b/src/hex.ts index 5022410d7..80d0dd264 100644 --- a/src/hex.ts +++ b/src/hex.ts @@ -80,11 +80,11 @@ export function isValidHexAddress(possibleAddress: Hex) { } /** - * Encodes a passed hex string as an ERC-55 mixed-case checksum address. - * Specification: https://eips.ethereum.org/EIPS/eip-55. + * Encode a passed hex string as an ERC-55 mixed-case checksum address. * * @param address - The hex address to encode. * @returns The address encoded according to ERC-55. + * @see https://eips.ethereum.org/EIPS/eip-55 */ export function erc55EncodeAddress(address: Hex) { const unPrefixed = remove0x(address.toLowerCase()); From bb84d96fb8068d265dac8a90df4a693403d74c99 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 11 Jul 2023 16:58:07 +0200 Subject: [PATCH 4/5] refactor: rename erc55EncodeAddress function --- src/hex.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hex.ts b/src/hex.ts index 80d0dd264..0373992ff 100644 --- a/src/hex.ts +++ b/src/hex.ts @@ -86,7 +86,7 @@ export function isValidHexAddress(possibleAddress: Hex) { * @returns The address encoded according to ERC-55. * @see https://eips.ethereum.org/EIPS/eip-55 */ -export function erc55EncodeAddress(address: Hex) { +export function getChecksumAddress(address: Hex) { const unPrefixed = remove0x(address.toLowerCase()); const unPrefixedHash = remove0x(bytesToHex(keccak256(unPrefixed))); return `0x${unPrefixed @@ -111,7 +111,7 @@ export function isValidChecksumAddress(possibleChecksum: Hex) { return false; } - return erc55EncodeAddress(possibleChecksum) === possibleChecksum; + return getChecksumAddress(possibleChecksum) === possibleChecksum; } /** From 4c13abef544588efdfb63902f98e79e08a0f2a22 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 11 Jul 2023 16:58:43 +0200 Subject: [PATCH 5/5] test: add test cases for getChecksumAddress --- src/hex.test.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/hex.test.ts b/src/hex.test.ts index 525388e2a..13abaeb2b 100644 --- a/src/hex.test.ts +++ b/src/hex.test.ts @@ -8,6 +8,7 @@ import { isStrictHexString, isValidHexAddress, remove0x, + getChecksumAddress, } from './hex'; describe('isHexString', () => { @@ -192,6 +193,30 @@ describe('isValidHexAddress', () => { }); }); +describe('getChecksumAddress', () => { + it('returns the checksum address for a valid hex address', () => { + expect( + getChecksumAddress('0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed'), + ).toBe('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed'); + + expect( + getChecksumAddress('0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359'), + ).toBe('0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359'); + + expect( + getChecksumAddress('0x52908400098527886e0f7030069857d2e4169ee7'), + ).toBe('0x52908400098527886E0F7030069857D2E4169EE7'); + + expect( + getChecksumAddress('0xde709f2102306220921060314715629080e2fb77'), + ).toBe('0xde709f2102306220921060314715629080e2fb77'); + + expect( + getChecksumAddress('0x0000000000000000000000000000000000000000'), + ).toBe('0x0000000000000000000000000000000000000000'); + }); +}); + describe('isValidChecksumAddress', () => { it.each([ '0x0000000000000000000000000000000000000000' as Hex,