import { __assign } from "tslib";
// The async/queue library has a task-based interface for building a concurrent queue.
import assert from "assert";
import { isEqual } from "lodash";
import { isDefined } from "../utils";
import * as alchemy from "./alchemy";
import * as infura from "./infura";
import * as drpc from "./drpc";
/**
 * Infura DIN is identified separately to allow it to be configured explicitly.
 */
var PROVIDERS = {
    ALCHEMY: alchemy.getURL,
    INFURA: infura.getURL,
    INFURA_DIN: infura.getURL,
    DRPC: drpc.getURL,
};
/**
 * Type predicate for RPCProvider type.
 * @param provider Provider string (ALCHEMY, INFURA, ...).
 * @returns True if the provider string is a supported provider.
 */
export function isSupportedProvider(provider) {
    return ["ALCHEMY", "INFURA", "INFURA_DIN", "DRPC"].includes(provider);
}
/**
 * Produce an RPC for a given RPC provider, chainId API key and transport.
 * @param provider RPC provider identifier (ALCHEMY, INFURA, ...)
 * @param chainId Chain ID to obtain a URL for.
 * @param apiKey API key for provider.
 * @param transport Optional transport specifier (HTTPS or WSS).
 * @returns An RPC URL confirming to the specified inputs.
 */
export function getURL(provider, chainId, apiKey, transport) {
    if (transport === void 0) { transport = "https"; }
    var getURL = PROVIDERS[provider];
    assert(getURL, "Unsupported RPC provider (".concat(provider, ")"));
    return getURL(chainId, apiKey, transport);
}
/**
 * Deletes keys from an object and returns new copy of object without ignored keys
 * @param ignoredKeys
 * @param obj
 * @returns Objects with ignored keys removed
 */
function deleteIgnoredKeys(ignoredKeys, obj) {
    if (!isDefined(obj)) {
        return;
    }
    var newObj = __assign({}, obj);
    for (var _i = 0, ignoredKeys_1 = ignoredKeys; _i < ignoredKeys_1.length; _i++) {
        var key = ignoredKeys_1[_i];
        delete newObj[key];
    }
    return newObj;
}
export function compareResultsAndFilterIgnoredKeys(ignoredKeys, _objA, _objB) {
    // Remove ignored keys from copied objects.
    var filteredA = deleteIgnoredKeys(ignoredKeys, _objA);
    var filteredB = deleteIgnoredKeys(ignoredKeys, _objB);
    // Compare objects without the ignored keys.
    return isEqual(filteredA, filteredB);
}
export function compareArrayResultsWithIgnoredKeys(ignoredKeys, objA, objB) {
    // Remove ignored keys from each element of copied arrays.
    var filteredA = objA === null || objA === void 0 ? void 0 : objA.map(function (obj) { return deleteIgnoredKeys(ignoredKeys, obj); });
    var filteredB = objB === null || objB === void 0 ? void 0 : objB.map(function (obj) { return deleteIgnoredKeys(ignoredKeys, obj); });
    // Compare objects without the ignored keys.
    return isDefined(filteredA) && isDefined(filteredB) && isEqual(filteredA, filteredB);
}
/**
 * A record of error codes that correspond to fields that should be ignored when comparing RPC results.
 * This is used to compare results from different providers that may have different, but still valid, results.
 */
var IGNORED_FIELDS = {
    // We've seen some RPC's like QuickNode add in transactionLogIndex which isn't in the
    // JSON RPC spec: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getfilterchanges
    // Additional reference: https://github.com/ethers-io/ethers.js/issues/1721
    // 2023-08-31 Added blockHash because of upstream zkSync provider disagreements. Consider removing later.
    // 2024-05-07 Added l1BatchNumber and logType due to Alchemy. Consider removing later.
    // 2024-07-11 Added blockTimestamp after zkSync rolled out a new node release.
    eth_getBlockByNumber: [
        "miner",
        "l1BatchNumber",
        "l1BatchTimestamp",
        "size",
        "totalDifficulty",
        "logsBloom",
        "transactions", // Polygon yParity field in transactions[]
    ],
    eth_getLogs: ["blockTimestamp", "transactionLogIndex", "l1BatchNumber", "logType"],
};
/**
 * A helper function to format an error message for a provider.
 * @param provider The provider that failed.
 * @param rawErrorText The raw error text.
 * @returns The formatted error message.
 */
export function formatProviderError(provider, rawErrorText) {
    return "Provider ".concat(provider.connection.url, " failed with error: ").concat(rawErrorText);
}
export function createSendErrorWithMessage(message, sendError) {
    var error = new Error(message);
    return __assign(__assign({}, sendError), error);
}
/**
 * Compares two RPC results, filtering out fields that are known to differ between providers.
 * Note: this function references `IGNORED_ERROR_CODES` which is a record of error codes that correspond to fields
 *       that should be ignored when comparing RPC results.
 * @param method The method that was called - conditionally filters out fields based on the method.
 * @param rpcResultA The first RPC result.
 * @param rpcResultB The second RPC result.
 * @returns True if the results are equal, false otherwise.
 */
export function compareRpcResults(method, rpcResultA, rpcResultB) {
    if (method === "eth_getBlockByNumber") {
        // We've seen RPC's disagree on the miner field, for example when Polygon nodes updated software that
        // led alchemy and quicknode to disagree on the miner field's value.
        return compareResultsAndFilterIgnoredKeys(IGNORED_FIELDS.eth_getBlockByNumber, rpcResultA, rpcResultB);
    }
    else if (method === "eth_getLogs") {
        return compareArrayResultsWithIgnoredKeys(IGNORED_FIELDS.eth_getLogs, rpcResultA, rpcResultB);
    }
    else {
        return isEqual(rpcResultA, rpcResultB);
    }
}
export var CacheType;
(function (CacheType) {
    CacheType[CacheType["NONE"] = 0] = "NONE";
    CacheType[CacheType["WITH_TTL"] = 1] = "WITH_TTL";
    CacheType[CacheType["NO_TTL"] = 2] = "NO_TTL";
    CacheType[CacheType["DECIDE_TTL_POST_SEND"] = 3] = "DECIDE_TTL_POST_SEND";
})(CacheType || (CacheType = {}));
//# sourceMappingURL=utils.js.map