import assert from "assert";
import { CHAIN_IDs } from "../constants";
import { BigNumber, fixedPointAdjustment, isEvmProvider, toBNWei, isDefined } from "../utils";
import { getPublicClient } from "./util";
import * as arbitrum from "./adapters/arbitrum";
import * as ethereum from "./adapters/ethereum";
import * as polygon from "./adapters/polygon";
import * as lineaViem from "./adapters/linea-viem";
import * as solana from "./adapters/solana";
const GAS_PRICE_ESTIMATE_DEFAULTS = {
    legacyFallback: true,
};
// Chains that use the Viem gas price oracle.
const VIEM_CHAINS = [CHAIN_IDs.LINEA];
/**
 * Provide an estimate for the current gas price for a particular chain.
 * @param provider A valid ethers provider.
 * @param {GasPriceEstimateOptions} opts optional parameters.
 * @returns An  object of type GasPriceEstimate.
 */
export async function getGasPriceEstimate(provider, opts = {}) {
    const baseFeeMultiplier = opts.baseFeeMultiplier ?? toBNWei("1");
    const priorityFeeMultiplier = opts.priorityFeeMultiplier ?? toBNWei("1");
    // Exit here if we need to estimate on Solana.
    if (!isEvmProvider(provider)) {
        const optsWithDefaults = {
            ...GAS_PRICE_ESTIMATE_DEFAULTS,
            baseFeeMultiplier,
            priorityFeeMultiplier,
            ...opts,
            chainId: opts.chainId ?? CHAIN_IDs.SOLANA,
        };
        return solana.messageFee(provider, optsWithDefaults);
    }
    // Cast the provider to an ethers provider, which should be given to the oracle when querying an EVM network.
    const chainId = opts.chainId ?? (await provider.getNetwork()).chainId;
    const optsWithDefaults = {
        ...GAS_PRICE_ESTIMATE_DEFAULTS,
        baseFeeMultiplier,
        priorityFeeMultiplier,
        ...opts,
        chainId,
    };
    // We only use the unsignedTx in the viem flow.
    const useViem = VIEM_CHAINS.includes(chainId);
    return useViem ? getGasPriceViem(chainId, optsWithDefaults) : getGasPriceEthers(provider, optsWithDefaults);
}
/**
 * Provide an estimate for the current gas price for a particular chain.
 * @param chainId The chain ID to query for gas prices.
 * @param provider A valid ethers provider.
 * @param legacyFallback In the case of an unrecognised chain, fall back to type 0 gas estimation.
 * @returns An object of type GasPriceEstimate.
 */
function getGasPriceEthers(provider, opts) {
    const { chainId, legacyFallback } = opts;
    // There shouldn't be any chains in here that we have a Viem adapter for because we'll always use Viem in that case.
    assert(!VIEM_CHAINS.includes(chainId), `Chain ID ${chainId} will use Viem gas price estimation`);
    const gasPriceFeeds = {
        [CHAIN_IDs.ARBITRUM]: arbitrum.eip1559,
        [CHAIN_IDs.POLYGON]: polygon.gasStation,
        [CHAIN_IDs.TEMPO]: ethereum.eip1559,
        [CHAIN_IDs.TRON]: ethereum.legacy,
        [CHAIN_IDs.ZK_SYNC]: ethereum.legacy,
        // Testnet
        [CHAIN_IDs.POLYGON_AMOY]: polygon.gasStation,
    };
    // If we have specified feeHistory parameters, then assume we want to take the feeHistory estimation path.
    let gasPriceFeed = isDefined(opts.feeHistoryOptions) ? ethereum.feeHistory : gasPriceFeeds[chainId];
    assert(gasPriceFeed || legacyFallback, `No suitable gas price oracle for Chain ID ${chainId}`);
    gasPriceFeed ??= ethereum.eip1559;
    return gasPriceFeed(provider, opts);
}
/**
 * Provide an estimate for the current gas price for a particular chain.
 * @param providerOrChainId A valid ethers provider or a chain ID.
 * @param transport An optional Viem Transport object for custom gas price retrieval.
 * @param unsignedTx Only used in Linea provider to estimate priority gas fee.
 * @returns An object of type GasPriceEstimate.
 */
async function getGasPriceViem(chainId, opts) {
    const { baseFeeMultiplier, transport } = opts;
    const viemProvider = getPublicClient(chainId, transport);
    const gasPriceFeeds = {
        [CHAIN_IDs.LINEA]: lineaViem.eip1559,
    };
    let maxFeePerGas;
    let maxPriorityFeePerGas;
    if (gasPriceFeeds[chainId]) {
        ({ maxFeePerGas, maxPriorityFeePerGas } = await gasPriceFeeds[chainId](viemProvider, opts));
    }
    else {
        let gasPrice;
        ({ maxFeePerGas, maxPriorityFeePerGas, gasPrice } = await viemProvider.estimateFeesPerGas());
        maxFeePerGas ??= (gasPrice * BigInt(baseFeeMultiplier.toString())) / BigInt(fixedPointAdjustment.toString());
        maxPriorityFeePerGas ??= BigInt(0);
    }
    return {
        maxFeePerGas: BigNumber.from(maxFeePerGas.toString()),
        maxPriorityFeePerGas: BigNumber.from(maxPriorityFeePerGas.toString()),
    };
}
//# sourceMappingURL=oracle.js.map