"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.dogeBase58AddrToPkScript = exports.btcBase58AddrToPkScript = exports.xrpBase58Checksum = exports.btcBase58Checksum = exports.xrpBase58Decode = exports.xrpBase58Encode = exports.btcBase58Decode = exports.btcBase58Encode = exports.BTC_BASE_58_DICT_regex = exports.standardAddressHash = exports.fillWithDefault = exports.defaultMccLoggingObject = exports.defaultExceptionCallback = exports.defaultWarningCallback = exports.defaultLoggingCallback = exports.mccJsonStringify = exports.bytesAsHexToString = exports.bytesToString = exports.isValidHexString = exports.isValidBytes32HexPrefix = exports.isValidBytes32Hex = exports.toSnakeCase = exports.camelToSnakeCase = exports.toCamelCase = exports.toNumber = exports.toBN = exports.toHex32Bytes = exports.hexToBytes = exports.toHex = exports.isPrefixed0x = exports.prefix0x = exports.unPrefix0x = exports.sleepMs = exports.getSimpleRandom = exports.MccError = exports.ZERO_BYTES_32 = void 0;
const web3_1 = __importDefault(require("web3"));
const util_1 = require("util");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const camelCase = require("camelcase");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const safeStringify = require("fast-safe-stringify");
exports.ZERO_BYTES_32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
function MccError(error) {
    try {
        const message = mccJsonStringify(error);
        return new Error(message);
    }
    catch (thisError) {
        return new Error(`MCC stringify error ${thisError}`);
    }
}
exports.MccError = MccError;
function getSimpleRandom(maxnum) {
    return Math.floor(Math.random() * maxnum);
}
exports.getSimpleRandom = getSimpleRandom;
async function sleepMs(ms) {
    await new Promise((resolve) => setTimeout(() => resolve(), ms));
}
exports.sleepMs = sleepMs;
function unPrefix0x(tx) {
    if (!tx) {
        return "0x0";
    }
    return tx.startsWith("0x") ? tx.slice(2) : tx;
}
exports.unPrefix0x = unPrefix0x;
function prefix0x(tx) {
    if (!tx) {
        return "0x0";
    }
    return tx.startsWith("0x") ? tx : "0x" + tx;
}
exports.prefix0x = prefix0x;
function isPrefixed0x(tx) {
    if (!tx) {
        return false;
    }
    return tx.startsWith("0x") ? true : false;
}
exports.isPrefixed0x = isPrefixed0x;
function toHex(x) {
    return web3_1.default.utils.toHex(x);
}
exports.toHex = toHex;
// Convert a hex string to a byte array (???ALGO SPECIFIC???)
function hexToBytes(hex) {
    const bytes = [];
    for (let c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
    return new Uint8Array(bytes);
}
exports.hexToBytes = hexToBytes;
function toHex32Bytes(x) {
    return web3_1.default.utils.padLeft(toHex(x), 64);
}
exports.toHex32Bytes = toHex32Bytes;
function toBN(x, toZeroIfFails = false) {
    if (x && x.constructor && x.constructor.name === "BN")
        return x;
    try {
        return web3_1.default.utils.toBN(x);
    }
    catch (e) {
        if (toZeroIfFails) {
            return web3_1.default.utils.toBN(0);
        }
        throw MccError(e);
    }
}
exports.toBN = toBN;
function toNumber(x) {
    if (x === undefined || x === null)
        return undefined;
    if (x && x.constructor && x.constructor.name === "BN")
        return x.toNumber();
    return x;
}
exports.toNumber = toNumber;
function toCamelCase(obj) {
    const camelObject = {};
    for (const prop in obj) {
        if (typeof obj[prop] == "object") {
            camelObject[camelCase(prop)] = toCamelCase(obj[prop]);
        }
        else {
            camelObject[camelCase(prop)] = obj[prop];
        }
    }
    return camelObject;
}
exports.toCamelCase = toCamelCase;
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
function camelToSnakeCase(str, splitWith = "-") {
    return str.replace(/[A-Z]/g, (letter) => `${splitWith}${letter.toLowerCase()}`);
}
exports.camelToSnakeCase = camelToSnakeCase;
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
function toSnakeCase(obj, splitWith = "-") {
    const camelObject = {};
    for (const prop in obj) {
        if (typeof obj[prop] == "object") {
            camelObject[camelToSnakeCase(camelCase(prop), splitWith)] = toSnakeCase(obj[prop], splitWith);
        }
        else {
            camelObject[camelToSnakeCase(camelCase(prop), splitWith)] = obj[prop];
        }
    }
    return camelObject;
}
exports.toSnakeCase = toSnakeCase;
function isValidBytes32Hex(address) {
    return /^(0x|0X)?[0-9a-fA-F]{64}$/i.test(address);
}
exports.isValidBytes32Hex = isValidBytes32Hex;
function isValidBytes32HexPrefix(address) {
    return /^(0x|0X)?[0-9a-fA-F]{64}$/i.test(address);
}
exports.isValidBytes32HexPrefix = isValidBytes32HexPrefix;
function isValidHexString(maybeHexString) {
    return /^(0x|0X)?[0-9a-fA-F]*$/i.test(maybeHexString);
}
exports.isValidHexString = isValidHexString;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////// bytes to string (utf8) decoder //////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
const decoder = new util_1.TextDecoder("UTF-8");
// Bytes to hex string
function bytesToString(bytes) {
    const array = new Uint8Array(bytes);
    return decoder.decode(array);
}
exports.bytesToString = bytesToString;
// Bytes as hex string to string
function bytesAsHexToString(bytesString) {
    if (isValidHexString(bytesString)) {
        const array = new Uint8Array(hexToBytes(unPrefix0x(bytesString)));
        return prefix0x(decoder.decode(array));
    }
    return "";
}
exports.bytesAsHexToString = bytesAsHexToString;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////// json stringify wrapper //////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
function mccJsonStringify(toStringify) {
    const options = {
        depthLimit: 2,
        edgesLimit: 3,
    };
    return safeStringify(toStringify, null, 2, options);
}
exports.mccJsonStringify = mccJsonStringify;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////// Default logging and exception callbacks /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
function defaultLoggingCallback(message) {
    console.log(message);
}
exports.defaultLoggingCallback = defaultLoggingCallback;
function defaultWarningCallback(message) {
    console.log(message);
}
exports.defaultWarningCallback = defaultWarningCallback;
function defaultExceptionCallback(error, message) {
    console.log(message);
    console.error(error);
    if (error.stack) {
        console.error(error.stack);
    }
}
exports.defaultExceptionCallback = defaultExceptionCallback;
function defaultMccLoggingObject() {
    return {
        mode: "develop",
        loggingCallback: defaultLoggingCallback,
        warningCallback: defaultWarningCallback,
        exceptionCallback: defaultExceptionCallback,
    };
}
exports.defaultMccLoggingObject = defaultMccLoggingObject;
function fillWithDefault(partialMccLogging) {
    return {
        mode: partialMccLogging.mode ? partialMccLogging.mode : "develop",
        loggingCallback: partialMccLogging.loggingCallback ? partialMccLogging.loggingCallback : defaultLoggingCallback,
        warningCallback: partialMccLogging.warningCallback ? partialMccLogging.warningCallback : defaultWarningCallback,
        exceptionCallback: partialMccLogging.exceptionCallback ? partialMccLogging.exceptionCallback : defaultExceptionCallback,
    };
}
exports.fillWithDefault = fillWithDefault;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////// Standard address Hash ///////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
function standardAddressHash(address) {
    return web3_1.default.utils.keccak256(address);
}
exports.standardAddressHash = standardAddressHash;
// Base 58 encoding/decoding
const base_x_1 = __importDefault(require("base-x"));
const errors_1 = require("./errors");
const crypto_1 = require("crypto");
const XRP_BASE_58_DICT = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
const BTC_BASE_58_DICT = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
exports.BTC_BASE_58_DICT_regex = /[^123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]/;
const btcBase58 = (0, base_x_1.default)(BTC_BASE_58_DICT);
const xrpBase58 = (0, base_x_1.default)(XRP_BASE_58_DICT);
function btcBase58Encode(input) {
    return btcBase58.encode(input);
}
exports.btcBase58Encode = btcBase58Encode;
function btcBase58Decode(input) {
    return btcBase58.decode(input);
}
exports.btcBase58Decode = btcBase58Decode;
function xrpBase58Encode(input) {
    return xrpBase58.encode(input);
}
exports.xrpBase58Encode = xrpBase58Encode;
function xrpBase58Decode(input) {
    return xrpBase58.decode(input);
}
exports.xrpBase58Decode = xrpBase58Decode;
/**
 *
 * @param address a base 58 address
 */
function btcBase58Checksum(address) {
    let decoded = btcBase58Decode(address);
    const preChecksum = decoded.slice(-4);
    const hash1 = (0, crypto_1.createHash)("sha256").update(decoded.slice(0, -4)).digest();
    const hash2 = (0, crypto_1.createHash)("sha256").update(hash1).digest();
    const newChecksum = hash2.slice(0, 4);
    return preChecksum.equals(newChecksum);
}
exports.btcBase58Checksum = btcBase58Checksum;
function xrpBase58Checksum(address) {
    let decoded = xrpBase58Decode(address);
    const preChecksum = decoded.slice(-4);
    const hash1 = (0, crypto_1.createHash)("sha256").update(decoded.slice(0, -4)).digest();
    const hash2 = (0, crypto_1.createHash)("sha256").update(hash1).digest();
    const newChecksum = hash2.slice(0, 4);
    return preChecksum.equals(newChecksum);
}
exports.xrpBase58Checksum = xrpBase58Checksum;
/**
 * Transforms a valid base58 btc address into a corresponding pkscript: either p2pkh or p2sh
 * @param address
 * @returns
 */
function btcBase58AddrToPkScript(address) {
    const prefix = address[0];
    const hexAddr = btcBase58Decode(address).toString("hex");
    //remove prefix and checksum bytes
    const strippedAddr = hexAddr.slice(2, -8);
    const len = unPrefix0x(web3_1.default.utils.padLeft(toHex(strippedAddr.length / 2), 2));
    switch (prefix) {
        case "1":
        case "n":
        case "m":
            return ["76", "a9", len, strippedAddr, "88", "ac"].join("");
        case "3":
        case "2":
            return ["a9", len, strippedAddr, "87"].join("");
        default:
            throw new errors_1.mccError(errors_1.mccErrorCode.InvalidParameter, Error("invalid prefix"));
    }
}
exports.btcBase58AddrToPkScript = btcBase58AddrToPkScript;
/**
 * Transforms a valid base58 doge address into a corresponding pkscript: either p2pkh or p2sh
 * @param address
 * @returns
 */
function dogeBase58AddrToPkScript(address) {
    const prefix = address[0];
    const hexAddr = btcBase58Decode(address).toString("hex");
    //remove prefix and checksum bytes
    const strippedAddr = hexAddr.slice(2, -8);
    const len = unPrefix0x(web3_1.default.utils.padLeft(toHex(strippedAddr.length / 2), 2));
    switch (prefix) {
        case "D":
        case "n":
        case "m":
            return ["76", "a9", len, strippedAddr, "88", "ac"].join("");
        case "A":
        case "9":
        case "2":
            return ["a9", len, strippedAddr, "87"].join("");
        default:
            throw new errors_1.mccError(errors_1.mccErrorCode.InvalidParameter, Error("invalid prefix"));
    }
}
exports.dogeBase58AddrToPkScript = dogeBase58AddrToPkScript;
//# sourceMappingURL=utils.js.map