Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/hex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
isStrictHexString,
isValidHexAddress,
remove0x,
getChecksumAddress,
} from './hex';

describe('isHexString', () => {
Expand Down Expand Up @@ -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,
Expand Down
37 changes: 21 additions & 16 deletions src/hex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@ export function isValidHexAddress(possibleAddress: Hex) {
);
}

/**
* 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 getChecksumAddress(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.
Expand All @@ -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 getChecksumAddress(possibleChecksum) === possibleChecksum;
}

/**
Expand Down
53 changes: 23 additions & 30 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down