import { __awaiter, __generator } from "tslib";
import assert from "assert";
import { BigNumber, bnZero, fixedPointAdjustment, getNetworkName, bnOne, isDefined, toBN } from "../../utils";
import { gasPriceError } from "../util";
/**
 * @dev Constructs total fee by adding eth_getBlock("pending").baseFee to eth_maxPriorityFeePerGas
 * @param provider ethers RPC provider instance.
 * @param {GasPriceEstimateOptions} opts See notes below on specific parameters.
 * @param baseFeeMultiplier Amount to multiply base fee or total fee for legacy gas pricing.
 * @param priorityFeeMultiplier Amount to multiply priority fee or unused for legacy gas pricing.
 * @returns Promise of gas price estimate object.
 */
export function eip1559(provider, opts) {
    return eip1559Raw(provider, opts.chainId, opts.baseFeeMultiplier, opts.priorityFeeMultiplier);
}
/**
 * @note Performs direct RPC calls to retrieve the RPC-suggested priority fee for the next block.
 * @dev Constructs total fee by adding eth_getBlock("pending").baseFee to eth_maxPriorityFeePerGas
 * @param provider ethers RPC provider instance.
 * @param chainId Chain ID of the provider instance.
 * @returns Promise of gas price estimate object.
 */
export function eip1559Raw(provider, chainId, baseFeeMultiplier, priorityFeeMultiplier) {
    return __awaiter(this, void 0, void 0, function () {
        var _a, baseFeePerGas, _maxPriorityFeePerGas, maxPriorityFeePerGas, scaledPriorityFee, scaledBaseFee;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, Promise.all([
                        provider.getBlock("latest"),
                        provider.send("eth_maxPriorityFeePerGas", []),
                    ])];
                case 1:
                    _a = _b.sent(), baseFeePerGas = _a[0].baseFeePerGas, _maxPriorityFeePerGas = _a[1];
                    maxPriorityFeePerGas = BigNumber.from(_maxPriorityFeePerGas);
                    assert(BigNumber.isBigNumber(baseFeePerGas), "No baseFeePerGas received on ".concat(getNetworkName(chainId)));
                    scaledPriorityFee = maxPriorityFeePerGas.mul(priorityFeeMultiplier).div(fixedPointAdjustment);
                    scaledBaseFee = baseFeePerGas.mul(baseFeeMultiplier).div(fixedPointAdjustment);
                    return [2 /*return*/, {
                            maxFeePerGas: scaledPriorityFee.add(scaledBaseFee),
                            maxPriorityFeePerGas: scaledPriorityFee,
                        }];
            }
        });
    });
}
/**
 * @notice Derives an appropriate maxPriorityFeePerGas by applying a predicate to historical rewards specified by eth_feeHistory.
 * @param provider ethers RPC provider instance.
 * @param {GasPriceEstimateOptions} Gas scaling options.
 */
export function feeHistory(provider, opts) {
    return __awaiter(this, void 0, void 0, function () {
        var baseFeeMultiplier, feeHistoryOptions, _a, percentile, _b, blockLookback, _c, minimumPriority, _d, baseFeePerGas, feeHistory, defaultEstimator, _e, estimator, maxPriorityFeePerGas, scaledBaseFee;
        return __generator(this, function (_f) {
            switch (_f.label) {
                case 0:
                    baseFeeMultiplier = opts.baseFeeMultiplier, feeHistoryOptions = opts.feeHistoryOptions;
                    assert(isDefined(feeHistoryOptions)); // We can only get here normally if feeHistoryOptions is defined.
                    _a = feeHistoryOptions.percentile, percentile = _a === void 0 ? 20 : _a, _b = feeHistoryOptions.blockLookback, blockLookback = _b === void 0 ? 10 : _b, _c = feeHistoryOptions.minimumPriority, minimumPriority = _c === void 0 ? bnOne : _c;
                    return [4 /*yield*/, Promise.all([
                            provider.getBlock("latest"),
                            provider.send("eth_feeHistory", [
                                Number(blockLookback),
                                "latest",
                                [Number(percentile)],
                            ]),
                        ])];
                case 1:
                    _d = _f.sent(), baseFeePerGas = _d[0].baseFeePerGas, feeHistory = _d[1];
                    assert(BigNumber.isBigNumber(baseFeePerGas), "No baseFeePerGas received on latest block query.");
                    defaultEstimator = function (rewards) {
                        var sortedRewards = rewards
                            .map(function (reward) { return toBN(reward); })
                            .filter(function (reward) { return reward.gt(bnZero); })
                            .sort(function (r1, r2) {
                            if (r1.gt(r2)) {
                                return 1;
                            }
                            return r1.eq(r2) ? 0 : -1;
                        });
                        // If all historical rewards are 0, then return the specified minimum.
                        var n = sortedRewards.length;
                        if (n === 0) {
                            return minimumPriority;
                        }
                        var median = n % 2 === 0 ? sortedRewards[n / 2 - 1].add(sortedRewards[n / 2]).div(2) : sortedRewards[Math.floor(n / 2)];
                        return median.gt(minimumPriority) ? median : minimumPriority;
                    };
                    _e = feeHistoryOptions.estimator, estimator = _e === void 0 ? defaultEstimator : _e;
                    maxPriorityFeePerGas = estimator(feeHistory.reward);
                    scaledBaseFee = baseFeePerGas.mul(baseFeeMultiplier).div(fixedPointAdjustment);
                    return [2 /*return*/, {
                            maxFeePerGas: scaledBaseFee.add(maxPriorityFeePerGas),
                            maxPriorityFeePerGas: maxPriorityFeePerGas,
                        }];
            }
        });
    });
}
/**
 * @notice Returns result of eth_gasPrice RPC call
 * @dev Its recommended to use the eip1559Raw method over this one where possible as it will be more accurate.
 * @returns GasPriceEstimate
 */
export function legacy(provider, opts) {
    return __awaiter(this, void 0, void 0, function () {
        var chainId, baseFeeMultiplier, gasPrice;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    chainId = opts.chainId, baseFeeMultiplier = opts.baseFeeMultiplier;
                    return [4 /*yield*/, provider.getGasPrice()];
                case 1:
                    gasPrice = _a.sent();
                    if (!BigNumber.isBigNumber(gasPrice) || gasPrice.lt(bnZero))
                        gasPriceError("getGasPrice()", chainId, gasPrice);
                    return [2 /*return*/, {
                            maxFeePerGas: gasPrice.mul(baseFeeMultiplier).div(fixedPointAdjustment),
                            maxPriorityFeePerGas: bnZero,
                        }];
            }
        });
    });
}
//# sourceMappingURL=ethereum.js.map