import { __assign, __awaiter, __generator } from "tslib";
import assert from "assert";
import { getComputeUnitEstimateForTransactionMessageFactory } from "@solana/kit";
import { SolanaVoidSigner, getFillRelayTx } from "../../arch/svm";
import { Coingecko } from "../../coingecko";
import { CHAIN_IDs } from "../../constants";
import { getGasPriceEstimate } from "../../gasPriceOracle";
import { toBN } from "../../utils";
import { getDefaultRelayer } from "../relayFeeCalculator";
/**
 * A special QueryBase implementation for SVM used for querying gas costs, token prices, and decimals of various tokens
 * on Solana.
 */
var SvmQuery = /** @class */ (function () {
    /**
     * Instantiates a SvmQuery instance
     * @param provider A valid solana/kit rpc client.
     * @param symbolMapping A mapping to valid ERC20 tokens and their respective characteristics
     * @param spokePool The valid address of the Spoke Pool deployment
     * @param simulatedRelayerAddress The address that these queries will reference as the sender. Note: This address must be approved for USDC
     * @param logger A logging utility to report logs
     * @param coingeckoProApiKey An optional CoinGecko API key that links to a PRO account
     * @param fixedGasPrice Overrides the gas price with a fixed value. Note: primarily used for the Boba blockchain
     * @param coingeckoBaseCurrency The basis currency that CoinGecko will use to resolve pricing
     */
    function SvmQuery(provider, symbolMapping, spokePool, simulatedRelayerAddress, logger, coingeckoProApiKey, fixedGasPrice, coingeckoBaseCurrency) {
        if (coingeckoBaseCurrency === void 0) { coingeckoBaseCurrency = "eth"; }
        this.provider = provider;
        this.symbolMapping = symbolMapping;
        this.spokePool = spokePool;
        this.simulatedRelayerAddress = simulatedRelayerAddress;
        this.logger = logger;
        this.coingeckoProApiKey = coingeckoProApiKey;
        this.fixedGasPrice = fixedGasPrice;
        this.coingeckoBaseCurrency = coingeckoBaseCurrency;
        this.computeUnitEstimator = getComputeUnitEstimateForTransactionMessageFactory({
            rpc: provider,
        });
    }
    /**
     * Retrieves the current gas costs of performing a fillRelay contract at the referenced SpokePool.
     * @param relayData RelayData instance, supplemented with destinationChainId
     * @param _relayer Relayer address to simulate with.
     * @param options
     * @param options.gasPrice Optional gas price to use for the simulation.
     * @param options.gasUnits Optional gas units to use for the simulation.
     * @param options.transport Optional transport object for custom gas price retrieval.
     * @returns The gas estimate for this function call (multiplied with the optional buffer).
     */
    SvmQuery.prototype.getGasCosts = function (relayData, relayer, options) {
        if (relayer === void 0) { relayer = getDefaultRelayer(relayData.destinationChainId); }
        if (options === void 0) { options = {}; }
        return __awaiter(this, void 0, void 0, function () {
            var destinationChainId, recipient, outputToken, exclusiveRelayer, _a, repaymentChainId, repaymentAddress, fillRelayTx, _b, computeUnitsConsumed, gasPriceEstimate, _c, _d, _e, gasPrice;
            return __generator(this, function (_f) {
                switch (_f.label) {
                    case 0:
                        destinationChainId = relayData.destinationChainId, recipient = relayData.recipient, outputToken = relayData.outputToken, exclusiveRelayer = relayData.exclusiveRelayer;
                        assert(recipient.isSVM(), "getGasCosts: recipient not an SVM address (".concat(recipient, ")"));
                        assert(outputToken.isSVM(), "getGasCosts: outputToken not an SVM address (".concat(outputToken, ")"));
                        assert(exclusiveRelayer.isSVM(), "getGasCosts: exclusiveRelayer not an SVM address (".concat(exclusiveRelayer, ")"));
                        assert(relayer.isSVM());
                        _a = [destinationChainId, relayer], repaymentChainId = _a[0], repaymentAddress = _a[1];
                        return [4 /*yield*/, this.getFillRelayTx(__assign(__assign({}, relayData), { recipient: recipient, outputToken: outputToken, exclusiveRelayer: exclusiveRelayer }), SolanaVoidSigner(relayer.toBase58()), repaymentChainId, repaymentAddress)];
                    case 1:
                        fillRelayTx = _f.sent();
                        _d = (_c = Promise).all;
                        _e = toBN;
                        return [4 /*yield*/, this.computeUnitEstimator(fillRelayTx)];
                    case 2: return [4 /*yield*/, _d.apply(_c, [[
                                _e.apply(void 0, [_f.sent()]),
                                getGasPriceEstimate(this.provider, {
                                    unsignedTx: fillRelayTx,
                                    baseFeeMultiplier: options.baseFeeMultiplier,
                                    priorityFeeMultiplier: options.priorityFeeMultiplier,
                                })
                            ]])];
                    case 3:
                        _b = _f.sent(), computeUnitsConsumed = _b[0], gasPriceEstimate = _b[1];
                        gasPrice = gasPriceEstimate.baseFee.add(gasPriceEstimate.microLamportsPerComputeUnit.mul(computeUnitsConsumed).div(toBN(1000000)) // 1_000_000 microLamports/lamport.
                        );
                        return [2 /*return*/, {
                                nativeGasCost: computeUnitsConsumed,
                                tokenGasCost: gasPrice,
                                gasPrice: gasPrice,
                            }];
                }
            });
        });
    };
    /**
     * @notice Return the gas cost of a simulated transaction
     * @param fillRelayTx FillRelay transaction
     * @param relayer SVM address of the relayer
     * @returns Estimated gas cost in compute units
     */
    SvmQuery.prototype.getNativeGasCost = function (deposit, relayer) {
        if (relayer === void 0) { relayer = getDefaultRelayer(deposit.destinationChainId); }
        return __awaiter(this, void 0, void 0, function () {
            var destinationChainId, recipient, outputToken, exclusiveRelayer, _a, repaymentChainId, repaymentAddress, fillRelayTx, _b;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        destinationChainId = deposit.destinationChainId, recipient = deposit.recipient, outputToken = deposit.outputToken, exclusiveRelayer = deposit.exclusiveRelayer;
                        assert(recipient.isSVM(), "getNativeGasCost: recipient not an SVM address (".concat(recipient, ")"));
                        assert(outputToken.isSVM(), "getNativeGasCost: outputToken not an SVM address (".concat(outputToken, ")"));
                        assert(exclusiveRelayer.isSVM(), "getNativeGasCost: exclusiveRelayer not an SVM address (".concat(exclusiveRelayer, ")"));
                        assert(relayer.isSVM());
                        _a = [destinationChainId, relayer], repaymentChainId = _a[0], repaymentAddress = _a[1];
                        return [4 /*yield*/, this.getFillRelayTx(__assign(__assign({}, deposit), { recipient: recipient, outputToken: outputToken, exclusiveRelayer: exclusiveRelayer }), SolanaVoidSigner(relayer.toBase58()), repaymentChainId, repaymentAddress)];
                    case 1:
                        fillRelayTx = _c.sent();
                        _b = toBN;
                        return [4 /*yield*/, this.computeUnitEstimator(fillRelayTx)];
                    case 2: return [2 /*return*/, _b.apply(void 0, [_c.sent()])];
                }
            });
        });
    };
    /**
     * @notice Return the fillRelay transaction for a given deposit
     * @param relayData RelayData instance, supplemented with destinationChainId
     * @param relayer SVM address of the relayer
     * @returns FillRelay transaction
     */
    SvmQuery.prototype.getFillRelayTx = function (relayData, signer, repaymentChainId, repaymentAddress) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, getFillRelayTx(this.spokePool, this.provider, relayData, signer, repaymentChainId, repaymentAddress)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    /**
     * Retrieves the current price of a token
     * @param tokenSymbol A valid [CoinGecko-ID](https://api.coingecko.com/api/v3/coins/list)
     * @returns The resolved token price within the specified coingeckoBaseCurrency
     */
    SvmQuery.prototype.getTokenPrice = function (tokenSymbol) {
        return __awaiter(this, void 0, void 0, function () {
            var coingeckoInstance, _a, price;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        if (!this.symbolMapping[tokenSymbol])
                            throw new Error("".concat(tokenSymbol, " does not exist in mapping"));
                        coingeckoInstance = Coingecko.get(this.logger, this.coingeckoProApiKey);
                        return [4 /*yield*/, coingeckoInstance.getCurrentPriceByContract(this.symbolMapping[tokenSymbol].addresses[CHAIN_IDs.MAINNET], this.coingeckoBaseCurrency)];
                    case 1:
                        _a = _b.sent(), price = _a[1];
                        return [2 /*return*/, price];
                }
            });
        });
    };
    /**
     * Resolves the number of decimal places a token can have
     * @param tokenSymbol A valid Across-Enabled Token ID
     * @returns The number of decimals of precision for the corresponding tokenSymbol
     */
    SvmQuery.prototype.getTokenDecimals = function (tokenSymbol) {
        if (!this.symbolMapping[tokenSymbol])
            throw new Error("".concat(tokenSymbol, " does not exist in mapping"));
        return this.symbolMapping[tokenSymbol].decimals;
    };
    return SvmQuery;
}());
export { SvmQuery };
//# sourceMappingURL=svmQuery.js.map