UNPKG

@metamask/utils

Version:

Various JavaScript/TypeScript utilities of wide relevance to the MetaMask codebase

122 lines 4.25 kB
import { is, pattern, string } from "@metamask/superstruct"; import { keccak_256 as keccak256 } from "@noble/hashes/sha3"; import { assert } from "./assert.mjs"; import { bytesToHex } from "./bytes.mjs"; export const HexStruct = pattern(string(), /^(?:0x)?[0-9a-f]+$/iu); export const StrictHexStruct = pattern(string(), /^0x[0-9a-f]+$/iu); export const HexAddressStruct = pattern(string(), /^0x[0-9a-f]{40}$/u); export const HexChecksumAddressStruct = pattern(string(), /^0x[0-9a-fA-F]{40}$/u); /** * Check if a string is a valid hex string. * * @param value - The value to check. * @returns Whether the value is a valid hex string. */ export function isHexString(value) { return is(value, HexStruct); } /** * Strictly check if a string is a valid hex string. A valid hex string must * start with the "0x"-prefix. * * @param value - The value to check. * @returns Whether the value is a valid hex string. */ export function isStrictHexString(value) { return is(value, StrictHexStruct); } /** * Assert that a value is a valid hex string. * * @param value - The value to check. * @throws If the value is not a valid hex string. */ export function assertIsHexString(value) { assert(isHexString(value), 'Value must be a hexadecimal string.'); } /** * Assert that a value is a valid hex string. A valid hex string must start with * the "0x"-prefix. * * @param value - The value to check. * @throws If the value is not a valid hex string. */ export function assertIsStrictHexString(value) { assert(isStrictHexString(value), 'Value must be a hexadecimal string, starting with "0x".'); } /** * Validate that the passed prefixed hex string is an all-lowercase * hex address, or a valid mixed-case checksum address. * * @param possibleAddress - Input parameter to check against. * @returns Whether or not the input is a valid hex address. */ export function isValidHexAddress(possibleAddress) { return (is(possibleAddress, HexAddressStruct) || isValidChecksumAddress(possibleAddress)); } /** * 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) { assert(is(address, HexChecksumAddressStruct), 'Invalid hex address.'); const unPrefixed = remove0x(address.toLowerCase()); const unPrefixedHash = remove0x(bytesToHex(keccak256(unPrefixed))); return `0x${unPrefixed .split('') .map((character, nibbleIndex) => { const hashCharacter = unPrefixedHash[nibbleIndex]; assert(is(hashCharacter, string()), 'Hash shorter than address.'); return parseInt(hashCharacter, 16) > 7 ? character.toUpperCase() : character; }) .join('')}`; } /** * Validate that the passed hex string is a valid ERC-55 mixed-case * checksum address. * * @param possibleChecksum - The hex address to check. * @returns True if the address is a checksum address. */ export function isValidChecksumAddress(possibleChecksum) { if (!is(possibleChecksum, HexChecksumAddressStruct)) { return false; } return getChecksumAddress(possibleChecksum) === possibleChecksum; } /** * Add the `0x`-prefix to a hexadecimal string. If the string already has the * prefix, it is returned as-is. * * @param hexadecimal - The hexadecimal string to add the prefix to. * @returns The prefixed hexadecimal string. */ export function add0x(hexadecimal) { if (hexadecimal.startsWith('0x')) { return hexadecimal; } if (hexadecimal.startsWith('0X')) { return `0x${hexadecimal.substring(2)}`; } return `0x${hexadecimal}`; } /** * Remove the `0x`-prefix from a hexadecimal string. If the string doesn't have * the prefix, it is returned as-is. * * @param hexadecimal - The hexadecimal string to remove the prefix from. * @returns The un-prefixed hexadecimal string. */ export function remove0x(hexadecimal) { if (hexadecimal.startsWith('0x') || hexadecimal.startsWith('0X')) { return hexadecimal.substring(2); } return hexadecimal; } //# sourceMappingURL=hex.mjs.map