import { BaseHTTPAdapter } from "../../priceClient/adapters/baseAdapter";
import { bnZero, fixedPointAdjustment, isDefined, parseUnits } from "../../utils";
import { CHAIN_IDs } from "../../constants";
import { gasPriceError } from "../util";
import { eip1559 } from "./ethereum";
const { POLYGON } = CHAIN_IDs;
export class PolygonGasStation extends BaseHTTPAdapter {
    chainId;
    constructor({ chainId = POLYGON, host, timeout = 1500, retries = 1 } = {}) {
        host = host ?? chainId === POLYGON ? "gasstation.polygon.technology" : "gasstation-testnet.polygon.technology";
        super("Polygon Gas Station", host, { timeout, retries });
        this.chainId = chainId;
    }
    async getFeeData(strategy = "fast") {
        const gas = await this.query("v2", {});
        const gasPrice = gas?.[strategy];
        if (!this.isPolygon1559GasPrice(gasPrice)) {
            // @todo: generalise gasPriceError() to accept a reason/cause?
            gasPriceError("getFeeData()", this.chainId, bnZero);
        }
        [gasPrice.maxFee, gasPrice.maxPriorityFee].forEach((gasPrice) => {
            if (Number(gasPrice) < 0) {
                gasPriceError("getFeeData()", this.chainId, parseUnits(gasPrice.toString(), 9));
            }
        });
        const maxPriorityFeePerGas = parseUnits(gasPrice.maxPriorityFee.toString(), 9);
        const maxFeePerGas = parseUnits(gasPrice.maxFee.toString(), 9);
        return { maxPriorityFeePerGas, maxFeePerGas };
    }
    isPolygon1559GasPrice(gasPrice) {
        if (!isDefined(gasPrice)) {
            return false;
        }
        const _gasPrice = gasPrice;
        return [_gasPrice.maxPriorityFee, _gasPrice.maxFee].every((field) => ["number", "string"].includes(typeof field));
    }
}
class MockRevertingPolygonGasStation extends PolygonGasStation {
    getFeeData() {
        throw new Error();
    }
}
export const MockPolygonGasStationBaseFee = () => parseUnits("12", 9);
export const MockPolygonGasStationPriorityFee = () => parseUnits("1", 9);
class MockPolygonGasStation extends PolygonGasStation {
    getFeeData() {
        return Promise.resolve({
            maxPriorityFeePerGas: MockPolygonGasStationPriorityFee(),
            maxFeePerGas: MockPolygonGasStationBaseFee().add(MockPolygonGasStationPriorityFee()),
        });
    }
}
/**
 * @notice Returns the gas price suggested by the Polygon GasStation API or reconstructs it using
 * the eip1559() method as a fallback.
 * @param provider Ethers Provider.
 * @returns EvmGasPriceEstimate
 */
export async function gasStation(provider, opts) {
    const { chainId, baseFeeMultiplier, priorityFeeMultiplier } = opts;
    let gasStation;
    if (process.env.TEST_POLYGON_GAS_STATION === "true") {
        gasStation = new MockPolygonGasStation();
    }
    else if (process.env.TEST_REVERTING_POLYGON_GAS_STATION === "true") {
        gasStation = new MockRevertingPolygonGasStation();
    }
    else {
        gasStation = new PolygonGasStation({ chainId: chainId, timeout: 2000, retries: 0 });
    }
    let maxPriorityFeePerGas;
    let maxFeePerGas;
    try {
        ({ maxPriorityFeePerGas, maxFeePerGas } = await gasStation.getFeeData());
        // Assume that the maxFeePerGas already includes the priority fee, so back out the priority fee before applying
        // the baseFeeMultiplier.
        const baseFeeMinusPriorityFee = maxFeePerGas.sub(maxPriorityFeePerGas);
        const scaledBaseFee = baseFeeMinusPriorityFee.mul(baseFeeMultiplier).div(fixedPointAdjustment);
        const scaledPriorityFee = maxPriorityFeePerGas.mul(priorityFeeMultiplier).div(fixedPointAdjustment);
        maxFeePerGas = scaledBaseFee.add(scaledPriorityFee);
        maxPriorityFeePerGas = scaledPriorityFee;
    }
    catch {
        // Fall back to the RPC provider. May be less accurate.
        ({ maxPriorityFeePerGas, maxFeePerGas } = await eip1559(provider, opts));
        // Per the GasStation docs, the minimum priority fee on Polygon is 30 Gwei.
        // https://docs.polygon.technology/tools/gas/polygon-gas-station/#interpretation
        const minPriorityFee = parseUnits("30", 9).mul(priorityFeeMultiplier).div(fixedPointAdjustment);
        if (maxPriorityFeePerGas.lt(minPriorityFee)) {
            const priorityDelta = minPriorityFee.sub(maxPriorityFeePerGas);
            maxPriorityFeePerGas = minPriorityFee;
            maxFeePerGas = maxFeePerGas.add(priorityDelta);
        }
    }
    return { maxPriorityFeePerGas, maxFeePerGas };
}
//# sourceMappingURL=polygon.js.map