"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueryBase = void 0;
const tslib_1 = require("tslib");
const l2_provider_1 = require("@eth-optimism/sdk/dist/l2-provider");
const ethers_1 = require("ethers");
const coingecko_1 = require("../../coingecko");
const constants_1 = require("../../constants");
const typechain_1 = require("../../typechain");
const evm_1 = require("../../arch/evm");
const utils_1 = require("../../utils");
const assert_1 = tslib_1.__importDefault(require("assert"));
const relayFeeCalculator_1 = require("../relayFeeCalculator");
const gasPriceOracle_1 = require("../../gasPriceOracle");
const __1 = require("../..");
class QueryBase {
    provider;
    symbolMapping;
    spokePoolAddress;
    simulatedRelayerAddress;
    logger;
    coingeckoProApiKey;
    fixedGasPrice;
    coingeckoBaseCurrency;
    spokePool;
    constructor(provider, symbolMapping, spokePoolAddress, simulatedRelayerAddress, logger, coingeckoProApiKey, fixedGasPrice, coingeckoBaseCurrency = "eth") {
        this.provider = provider;
        this.symbolMapping = symbolMapping;
        this.spokePoolAddress = spokePoolAddress;
        this.simulatedRelayerAddress = simulatedRelayerAddress;
        this.logger = logger;
        this.coingeckoProApiKey = coingeckoProApiKey;
        this.fixedGasPrice = fixedGasPrice;
        this.coingeckoBaseCurrency = coingeckoBaseCurrency;
        this.spokePool = typechain_1.SpokePool__factory.connect(spokePoolAddress, provider);
    }
    async getGasCosts(relayData, relayer = (0, relayFeeCalculator_1.getDefaultRelayer)(relayData.destinationChainId), options = {}) {
        const { gasPrice = this.fixedGasPrice, gasUnits, opStackL1GasCostMultiplier } = options;
        const { recipient, outputToken, exclusiveRelayer } = relayData;
        (0, assert_1.default)(recipient.isEVM() || recipient.isTVM(), `getGasCosts: recipient not an EVM-like address (${recipient})`);
        (0, assert_1.default)(outputToken.isEVM() || outputToken.isTVM(), `getGasCosts: outputToken not an EVM-like address (${outputToken})`);
        (0, assert_1.default)(exclusiveRelayer.isEVM() || exclusiveRelayer.isTVM(), `getGasCosts: exclusiveRelayer not an EVM-like address (${exclusiveRelayer})`);
        const tx = await this.getUnsignedTxFromDeposit({ ...relayData, recipient, outputToken, exclusiveRelayer }, relayer);
        const { nativeGasCost, tokenGasCost, gasPrice: impliedGasPrice, opStackL1GasCost, } = await this.estimateGas(tx, relayer, this.provider, {
            ...options,
            gasPrice,
            gasUnits,
            opStackL1GasCostMultiplier,
        });
        return {
            nativeGasCost,
            tokenGasCost,
            gasPrice: impliedGasPrice,
            opStackL1GasCost,
        };
    }
    getUnsignedTxFromDeposit(relayData, relayer = (0, relayFeeCalculator_1.getDefaultRelayer)(relayData.destinationChainId)) {
        return (0, evm_1.populateV3Relay)(this.spokePool, relayData, relayer);
    }
    async getNativeGasCost(relayData, relayer = (0, relayFeeCalculator_1.getDefaultRelayer)(relayData.destinationChainId)) {
        const { recipient, outputToken, exclusiveRelayer } = relayData;
        (0, assert_1.default)(recipient.isEVM() || recipient.isTVM(), `getNativeGasCost: recipient not an EVM-like address (${recipient})`);
        (0, assert_1.default)(outputToken.isEVM() || outputToken.isTVM(), `getNativeGasCost: outputToken not an EVM-like address (${outputToken})`);
        (0, assert_1.default)(exclusiveRelayer.isEVM() || exclusiveRelayer.isTVM(), `getNativeGasCost: exclusiveRelayer not an EVM-like address (${exclusiveRelayer})`);
        const unsignedTx = await this.getUnsignedTxFromDeposit({ ...relayData, recipient, outputToken, exclusiveRelayer }, relayer);
        const voidSigner = new ethers_1.VoidSigner(relayer.toEvmAddress(), this.provider);
        return voidSigner.estimateGas(unsignedTx);
    }
    getAuxiliaryNativeTokenCost(_deposit) {
        return __1.arch.evm.getAuxiliaryNativeTokenCost(_deposit);
    }
    async getOpStackL1DataFee(unsignedTx, relayer = (0, relayFeeCalculator_1.getDefaultRelayer)(unsignedTx.chainId), options) {
        const { opStackL2GasUnits, opStackL1DataFeeMultiplier = (0, utils_1.toBNWei)("1") } = options || {};
        const { chainId } = await this.provider.getNetwork();
        (0, assert_1.default)((0, l2_provider_1.isL2Provider)(this.provider), `Unexpected provider for chain ID ${chainId}.`);
        const voidSigner = new ethers_1.VoidSigner(relayer.toEvmAddress(), this.provider);
        const populatedTransaction = await voidSigner.populateTransaction({
            ...unsignedTx,
            nonce: 0xffffffff,
            gasLimit: opStackL2GasUnits,
        });
        const l1DataFee = await this.provider.estimateL1GasCost(populatedTransaction);
        return l1DataFee.mul(opStackL1DataFeeMultiplier).div(utils_1.fixedPointAdjustment);
    }
    async estimateGas(unsignedTx, senderAddress, provider, options = {}) {
        const { gasPrice: _gasPrice, gasUnits, baseFeeMultiplier = (0, utils_1.toBNWei)("1"), priorityFeeMultiplier = (0, utils_1.toBNWei)("1"), opStackL1GasCostMultiplier = (0, utils_1.toBNWei)("1"), transport, } = options || {};
        const { chainId } = await provider.getNetwork();
        const voidSigner = new ethers_1.VoidSigner(senderAddress.toEvmAddress(), provider);
        const queries = [
            gasUnits ? Promise.resolve(utils_1.BigNumber.from(gasUnits)) : voidSigner.estimateGas(unsignedTx),
            _gasPrice
                ? Promise.resolve({ maxFeePerGas: _gasPrice })
                : (0, gasPriceOracle_1.getGasPriceEstimate)(provider, {
                    ...options,
                    chainId,
                    baseFeeMultiplier,
                    priorityFeeMultiplier,
                    transport,
                    unsignedTx,
                }),
        ];
        const [nativeGasCost, gasPriceEstimate] = await Promise.all(queries);
        const gasPrice = gasPriceEstimate.maxFeePerGas;
        (0, assert_1.default)(nativeGasCost.gt(utils_1.bnZero), "Gas cost should not be 0");
        let tokenGasCost;
        let opStackL1GasCost;
        if ((0, utils_1.chainIsOPStack)(chainId)) {
            opStackL1GasCost = await this.getOpStackL1DataFee(unsignedTx, senderAddress, {
                opStackL2GasUnits: nativeGasCost,
                opStackL1DataFeeMultiplier: opStackL1GasCostMultiplier,
            });
            const l2GasCost = nativeGasCost.mul(gasPrice);
            tokenGasCost = opStackL1GasCost.add(l2GasCost);
        }
        else {
            tokenGasCost = nativeGasCost.mul(gasPrice);
        }
        return {
            nativeGasCost,
            tokenGasCost,
            gasPrice: utils_1.BigNumber.from(gasPrice.toString()),
            opStackL1GasCost,
        };
    }
    async getTokenPrice(tokenSymbol) {
        if (!this.symbolMapping[tokenSymbol])
            throw new Error(`${tokenSymbol} does not exist in mapping`);
        const coingeckoInstance = coingecko_1.Coingecko.get(this.logger, this.coingeckoProApiKey);
        const [, price] = await coingeckoInstance.getCurrentPriceByContract(this.symbolMapping[tokenSymbol].addresses[constants_1.CHAIN_IDs.MAINNET], this.coingeckoBaseCurrency);
        return price;
    }
}
exports.QueryBase = QueryBase;
//# sourceMappingURL=baseQuery.js.map