import { __awaiter, __extends, __generator } from "tslib";
import { utils } from "ethers";
import bs58 from "bs58";
import { BigNumber, chainIsEvm, chainIsSvm } from "./";
/**
 * Checks if a contract is deployed at the given address
 * @param address The ETH address to check
 * @param provider A valid Ethers.js provider
 * @returns A boolean indicating if a contract is deployed at the given address or not (true = contract, false = no contract)
 */
export function isContractDeployedToAddress(address, provider) {
    return __awaiter(this, void 0, void 0, function () {
        var code;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    // A base case for if the address is null or malformed
                    if (!address || !utils.isAddress(address)) {
                        return [2 /*return*/, false];
                    }
                    return [4 /*yield*/, provider.getCode(address)];
                case 1:
                    code = _a.sent();
                    // If the code is not empty, then there is a contract at this address
                    return [2 /*return*/, code !== "0x"];
            }
        });
    });
}
export function compareAddresses(addressA, addressB) {
    // Convert address strings to BigNumbers and then sort numerical value of the BigNumber, which sorts the addresses
    // effectively by their hex value.
    var bnAddressA = BigNumber.from(addressA);
    var bnAddressB = BigNumber.from(addressB);
    if (bnAddressA.gt(bnAddressB)) {
        return 1;
    }
    else if (bnAddressA.lt(bnAddressB)) {
        return -1;
    }
    else {
        return 0;
    }
}
export function compareAddressesSimple(addressA, addressB) {
    if (addressA === undefined || addressB === undefined) {
        return false;
    }
    return addressA.toLowerCase() === addressB.toLowerCase();
}
// Converts an input hex data string into a bytes32 string. Note that the output bytes will be lowercase
// so that it naturally matches with ethers event data.
// Throws an error if the input string is already greater than 32 bytes.
export function toBytes32(address) {
    return utils.hexZeroPad(address, 32).toLowerCase();
}
// Constructs a 20-byte checksummed EVM address from a hex string input.
// Throws an error if the underlying address length is longer than 20 bytes or incorrectly checksummed.
export function toEvmAddress(hexString) {
    // rawAddress is the address which is not properly checksummed.
    var rawAddress = utils.hexZeroPad(utils.hexStripZeros(hexString), 20);
    return utils.getAddress(rawAddress);
}
export function isValidEvmAddress(address) {
    if (utils.isAddress(address)) {
        return true;
    }
    // We may throw an error here if hexZeroPadFails. This will happen if the address to pad is greater than 20 bytes long, indicating
    // that the address had less than 12 leading zero bytes.
    // We may also throw at getAddress if the input cannot be converted into a checksummed EVM address for some reason.
    // For both cases, this indicates that the address cannot be casted as a bytes20 EVM address, so we should return false.
    try {
        var evmAddress = utils.hexZeroPad(utils.hexStripZeros(address), 20);
        return utils.isAddress(utils.getAddress(evmAddress));
    }
    catch (_e) {
        return false;
    }
}
/**
 * Creates the proper address type given the input chain ID corresponding to the address's origin network.
 * @param address Stringified address type to convert. Can be either hex encoded or base58 encoded.
 * @param chainId Chain ID for the intended use of the address.
 * @returns a child `Address` type most fitting for the chain ID.
 * @todo: Change this to `toAddress` once we remove the other `toAddress` function.
 */
export function toAddressType(address, chainId) {
    var rawAddress = address.startsWith("0x") ? utils.arrayify(address) : bs58.decode(address);
    if (chainIsEvm(chainId) && EvmAddress.validate(rawAddress))
        return new EvmAddress(rawAddress);
    else if (chainIsSvm(chainId) && SvmAddress.validate(rawAddress))
        return new SvmAddress(rawAddress);
    return new RawAddress(rawAddress);
}
// The Address class can contain any address type. It is up to the subclasses to determine how to format the address's internal representation,
// which for this class, is a bytes32 hex string.
var Address = /** @class */ (function () {
    function Address(_rawAddress) {
        this.__address_type_brand = true;
        // Keep all address types in cache so that we may lazily evaluate them when necessary.
        this.evmAddress = undefined;
        this.bytes32Address = undefined;
        this.svmAddress = undefined;
        this.bnAddress = undefined;
        // The only validation done here is checking that the address is at most a 32-byte address, which  will be well-defined on any supported network.
        // Further validation is done on the subclasses so that we can guarantee that, for example, an EvmAddress type will always contain a valid, 20-byte
        // EVM address.
        if (_rawAddress.length > 32) {
            throw new Error("Address ".concat(utils.hexlify(_rawAddress), " cannot be longer than 32 bytes."));
        }
        // Ensure all addresses in this class are internally stored as 32 bytes.
        this.rawAddress = utils.zeroPad(_rawAddress, 32);
    }
    Address.isAddress = function (obj) {
        return "__address_type_brand" in obj;
    };
    // Converts the address into a bytes32 string. Note that the output bytes will be lowercase so that it matches ethers event data. This function will never
    // throw since address length validation was done at construction time.
    Address.prototype.toBytes32 = function () {
        var _a;
        return ((_a = this.bytes32Address) !== null && _a !== void 0 ? _a : (this.bytes32Address = utils.hexZeroPad(utils.hexlify(this.rawAddress), 32).toLowerCase()));
    };
    // Returns last 20 bytes of Address as a hex string. Truncates if necessary. This function is useful for comparing some longer addresses(e.g. Solana) to Solidity events
    // that contain truncated `address` type as one of the fields
    Address.prototype.truncateToBytes20 = function () {
        // Take the last 20 bytes
        var bytes20 = this.rawAddress.slice(-20);
        return toEvmAddress(utils.hexlify(bytes20));
    };
    // Converts the address (can be bytes32 or bytes20) to its base58 counterpart. This conversion will always succeed, even if the input address is not valid on Solana,
    // as this address may be needed to represent an EVM address on Solana.
    Address.prototype.toBase58 = function () {
        var _a;
        return ((_a = this.svmAddress) !== null && _a !== void 0 ? _a : (this.svmAddress = bs58.encode(this.rawAddress)));
    };
    // Converts the address to a BigNumber type.
    Address.prototype.toBigNumber = function () {
        var _a;
        return ((_a = this.bnAddress) !== null && _a !== void 0 ? _a : (this.bnAddress = BigNumber.from(this.toBytes32())));
    };
    // Converts the address to a valid EVM address. If it is unable to convert the address to a valid EVM address for some reason, such as if this address
    // is longer than 20 bytes, then this function will throw an error.
    Address.prototype.toEvmAddress = function () {
        var _this = this;
        var _a;
        var parseRawAddress = function () {
            var hexString = utils.hexlify(_this.rawAddress);
            var rawAddress = utils.hexZeroPad(utils.hexStripZeros(hexString), 20);
            return utils.getAddress(rawAddress);
        };
        return ((_a = this.evmAddress) !== null && _a !== void 0 ? _a : (this.evmAddress = parseRawAddress()));
    };
    // Converts the address to a hex string. This method should be overriden by subclasses to obtain more meaningful
    // address representations for the target chain ID.
    Address.prototype.toNative = function () {
        return this.toBytes32();
    };
    // Implements `Hexable` for `Address`. Needed for encoding purposes. This class is treated by default as a bytes32 primitive type, but can change for subclasses.
    Address.prototype.toHexString = function () {
        return this.toBytes32();
    };
    // Checks if the address is valid on the given chain ID.
    Address.prototype.isValidOn = function (chainId) {
        if (chainIsEvm(chainId))
            return EvmAddress.validate(this.rawAddress);
        if (chainIsSvm(chainId))
            return SvmAddress.validate(this.rawAddress);
        return false;
    };
    // Converts the input address to a 32-byte hex data string.
    Address.prototype.toString = function () {
        return this.toNative();
    };
    // Checks if the address is the zero address.
    Address.prototype.isZeroAddress = function () {
        return utils.stripZeros(this.rawAddress).length === 0;
    };
    // Forces `rawAddress` to become an SvmAddress type. This will only throw if `rawAddress.length > 32`.
    Address.prototype.forceSvmAddress = function () {
        return SvmAddress.from(this.toBase58());
    };
    // Checks if the other address is equivalent to this address.
    Address.prototype.eq = function (other) {
        return this.toString() === other.toString();
    };
    // Compares Addresses by first converting them to BigNumbers.
    // note: Intended for use when sorting like addresses.
    Address.prototype.compare = function (otherAddress) {
        // Convert address strings to BigNumbers and then sort numerical value of the BigNumber, which sorts the addresses
        // effectively by their hex value.
        var bnAddressA = this.toBigNumber();
        var bnAddressB = otherAddress.toBigNumber();
        if (bnAddressA.gt(bnAddressB)) {
            return 1;
        }
        else if (bnAddressA.lt(bnAddressB)) {
            return -1;
        }
        else {
            return 0;
        }
    };
    Address.prototype.isEVM = function () {
        return false;
    };
    Address.prototype.isSVM = function () {
        return false;
    };
    return Address;
}());
export { Address };
// Subclass of address which strictly deals with 20-byte addresses. These addresses are guaranteed to be valid EVM addresses, so `toAddress` will always succeed.
var EvmAddress = /** @class */ (function (_super) {
    __extends(EvmAddress, _super);
    // On construction, validate that the address can indeed be coerced into an EVM address. Throw immediately if it cannot.
    function EvmAddress(rawAddress) {
        var _this = this;
        if (!EvmAddress.validate(rawAddress)) {
            throw new Error("".concat(utils.hexlify(rawAddress), " is not a valid EVM address"));
        }
        _this = _super.call(this, rawAddress) || this;
        _this._type = "evm";
        _this._type; // tsc noUnusedLocals appeasement.
        return _this;
    }
    EvmAddress.validate = function (rawAddress) {
        return (rawAddress.length == 20 || (rawAddress.length === 32 && rawAddress.slice(0, 12).every(function (field) { return field === 0; })));
    };
    EvmAddress.prototype.isEVM = function () {
        return true;
    };
    // Override `toAddress` to return the 20-byte representation address.
    EvmAddress.prototype.toNative = function () {
        return this.toEvmAddress();
    };
    // Constructs a new EvmAddress type.
    EvmAddress.from = function (address) {
        return address.startsWith("0x") ? new this(utils.arrayify(address)) : new this(bs58.decode(address));
    };
    return EvmAddress;
}(Address));
export { EvmAddress };
// Subclass of address which strictly deals SVM addresses. These addresses are guaranteed to be valid SVM addresses, so `toBase58` will always produce a valid Solana address.
var SvmAddress = /** @class */ (function (_super) {
    __extends(SvmAddress, _super);
    // On construction, validate that the address is a point on Curve25519. Throw immediately if it is not.
    function SvmAddress(rawAddress) {
        var _this = this;
        if (!SvmAddress.validate(rawAddress)) {
            throw new Error("".concat(utils.hexlify(rawAddress), " is not a valid SVM address")); // @todo: Display as Base58?
        }
        _this = _super.call(this, rawAddress) || this;
        _this._type = "svm";
        _this._type; // tsc noUnusedLocals appeasement.
        return _this;
    }
    SvmAddress.validate = function (rawAddress) {
        // Deliberately invalidate SVM addresses w/ the upper 12 bytes zeroed. These addresses are technically valid
        // but highly improbable and are much more likely to be a mistaken interpretation of an EVM address. Err on
        // the side of caution for the time being. Exception: Permit the zero address (i.e. for exclusiverRelayer).
        return (rawAddress.length === 32 &&
            (!rawAddress.slice(0, 12).every(function (field) { return field === 0; }) || rawAddress.every(function (field) { return field === 0; })));
    };
    SvmAddress.prototype.isSVM = function () {
        return true;
    };
    // Override the toAddress function for SVM addresses only since while they will never have a defined 20-byte representation. The base58 encoded addresses are also the encodings
    // used in TOKEN_SYMBOLS_MAP.
    SvmAddress.prototype.toNative = function () {
        return this.toBase58();
    };
    // Constructs a new SvmAddress type.
    SvmAddress.from = function (address) {
        return address.startsWith("0x") ? new this(utils.arrayify(address)) : new this(bs58.decode(address));
    };
    return SvmAddress;
}(Address));
export { SvmAddress };
var RawAddress = /** @class */ (function (_super) {
    __extends(RawAddress, _super);
    function RawAddress(rawAddress) {
        var _this = _super.call(this, rawAddress) || this;
        _this._type = "raw";
        _this._type; // tsc noUnusedLocals appeasement.
        return _this;
    }
    return RawAddress;
}(Address));
export { RawAddress };
//# sourceMappingURL=AddressUtils.js.map