UNPKG

ox

Version:

Ethereum Standard Library

80 lines (74 loc) 2.32 kB
/** @internal */ export const length = 42 const ZERO = 48 // '0' const NINE = 57 // '9' const A_UPPER = 65 // 'A' const F_UPPER = 70 // 'F' const A_LOWER = 97 // 'a' const F_LOWER = 102 // 'f' const X_LOWER = 120 // 'x' /** * Combined shape + lowercase classification for an Ethereum address string. * Avoids the regex / try-catch / error-allocation overhead in the hot path * and avoids a second pass for callers that need both pieces of metadata * (e.g. strict validation). * * - `0` = invalid shape (not `0x[0-9a-fA-F]{40}`) * - `1` = valid shape, all-lowercase hex body (no checksum to verify) * - `2` = valid shape, contains at least one `A-F` upper char * * @internal */ export function classify(value: string): 0 | 1 | 2 { if (typeof value !== 'string' || value.length !== length) return 0 if (value.charCodeAt(0) !== ZERO || value.charCodeAt(1) !== X_LOWER) return 0 let hasUpper = false for (let i = 2; i < length; i++) { const code = value.charCodeAt(i) if (code >= ZERO && code <= NINE) continue if (code >= A_LOWER && code <= F_LOWER) continue if (code >= A_UPPER && code <= F_UPPER) { hasUpper = true continue } return 0 } return hasUpper ? 2 : 1 } /** * Manual ASCII shape check for an Ethereum address string. Returns `true` * when `value` has the shape `0x[0-9a-fA-F]{40}`. Does not verify checksum * casing. * * @internal */ export function hasShape(value: string): boolean { return classify(value) !== 0 } /** * Returns the canonical lowercase form of a shape-validated address, or the * input unchanged when it is already all-lowercase. Pre-condition: caller has * already verified the string is shape-valid via {@link hasShape} or * {@link classify}. * * @internal */ export function lowercase(value: string): string { let hasUpper = false for (let i = 2; i < length; i++) { const code = value.charCodeAt(i) if (code >= A_UPPER && code <= F_UPPER) { hasUpper = true break } } if (!hasUpper) return value const out = new Uint16Array(length) out[0] = ZERO out[1] = X_LOWER for (let i = 2; i < length; i++) { const code = value.charCodeAt(i) out[i] = code >= A_UPPER && code <= F_UPPER ? code + 32 : code } return String.fromCharCode.apply(null, out as unknown as number[]) }