"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvalidChecksumError = exports.InvalidInputError = exports.InvalidAddressError = void 0;
exports.assert = assert;
exports.checksum = checksum;
exports.from = from;
exports.fromPublicKey = fromPublicKey;
exports.isEqual = isEqual;
exports.validate = validate;
const Bytes = require("./Bytes.js");
const Caches = require("./Caches.js");
const Errors = require("./Errors.js");
const Hash = require("./Hash.js");
const PublicKey = require("./PublicKey.js");
const addressRegex = /^0x[a-fA-F0-9]{40}$/;
function assert(value, options = {}) {
    const { strict = true } = options;
    if (!addressRegex.test(value))
        throw new InvalidAddressError({
            address: value,
            cause: new InvalidInputError(),
        });
    if (strict) {
        if (value.toLowerCase() === value)
            return;
        if (checksum(value) !== value)
            throw new InvalidAddressError({
                address: value,
                cause: new InvalidChecksumError(),
            });
    }
}
function checksum(address) {
    if (Caches.checksum.has(address))
        return Caches.checksum.get(address);
    assert(address, { strict: false });
    const hexAddress = address.substring(2).toLowerCase();
    const hash = Hash.keccak256(Bytes.fromString(hexAddress), { as: 'Bytes' });
    const characters = hexAddress.split('');
    for (let i = 0; i < 40; i += 2) {
        if (hash[i >> 1] >> 4 >= 8 && characters[i]) {
            characters[i] = characters[i].toUpperCase();
        }
        if ((hash[i >> 1] & 0x0f) >= 8 && characters[i + 1]) {
            characters[i + 1] = characters[i + 1].toUpperCase();
        }
    }
    const result = `0x${characters.join('')}`;
    Caches.checksum.set(address, result);
    return result;
}
function from(address, options = {}) {
    const { checksum: checksumVal = false } = options;
    assert(address);
    if (checksumVal)
        return checksum(address);
    return address;
}
function fromPublicKey(publicKey, options = {}) {
    const address = Hash.keccak256(`0x${PublicKey.toHex(publicKey).slice(4)}`).substring(26);
    return from(`0x${address}`, options);
}
function isEqual(addressA, addressB) {
    assert(addressA, { strict: false });
    assert(addressB, { strict: false });
    return addressA.toLowerCase() === addressB.toLowerCase();
}
function validate(address, options = {}) {
    const { strict = true } = options ?? {};
    try {
        assert(address, { strict });
        return true;
    }
    catch {
        return false;
    }
}
class InvalidAddressError extends Errors.BaseError {
    constructor({ address, cause }) {
        super(`Address "${address}" is invalid.`, {
            cause,
        });
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'Address.InvalidAddressError'
        });
    }
}
exports.InvalidAddressError = InvalidAddressError;
class InvalidInputError extends Errors.BaseError {
    constructor() {
        super('Address is not a 20 byte (40 hexadecimal character) value.');
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'Address.InvalidInputError'
        });
    }
}
exports.InvalidInputError = InvalidInputError;
class InvalidChecksumError extends Errors.BaseError {
    constructor() {
        super('Address does not match its checksum counterpart.');
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'Address.InvalidChecksumError'
        });
    }
}
exports.InvalidChecksumError = InvalidChecksumError;
//# sourceMappingURL=Address.js.map