ox
Version:
Ethereum Standard Library
80 lines • 2.46 kB
JavaScript
/** @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) {
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) {
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) {
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);
}
//# sourceMappingURL=address.js.map