"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processPendingOrders = processPendingOrders;
const ethers_1 = require("ethers");
const logger_1 = require("../../utils/logger");
const helpers_1 = require("../../utils/helpers");
/**
 * Processes orders that have not been bid on at all.
 * These are orders specifically pending for bids.
 */
async function processPendingOrders(executor) {
    const config = executor.getConfig();
    const pricerApi = executor.getPricerApi();
    const prometheus = executor.getPrometheus();
    const myAddress = executor.getMyAddress();
    logger_1.logger.info('🔃 Start processing pending orders in batches...');
    const sleepSec = executor.getPendingOrdersInterval();
    while (true) {
        await (0, helpers_1.sleep)(sleepSec, `Sleep between processing pending orders`);
        const enabledNetworksIds = executor.getEnabledNetworksIds();
        if (enabledNetworksIds.length === 0) {
            logger_1.logger.error('🚫 No networks enabled for executor. Exiting.');
            break;
        }
        if (executor.getHaltProcessingNewOrders()) {
            logger_1.logger.info('🚫 Processing new orders is currently halted.');
            continue;
        }
        const transmitterL3 = executor.getTransmitterL3();
        if (!transmitterL3) {
            logger_1.logger.error('Transmitter L3 not found');
            await (0, helpers_1.sleep)(sleepSec, `Sleep between processing pending orders`);
            continue;
        }
        let orders;
        try {
            orders = (await pricerApi.fetchOrdersPendingPerExecutor(myAddress));
        }
        catch {
            await (0, helpers_1.sleep)(sleepSec, `Sleep between processing pending orders`);
            continue;
        }
        const enabledNetworks = executor.enabledNetworksIds;
        if (orders.length === 0) {
            logger_1.logger.info({ sleepSec }, '🔃🏆 No pending orders to Bid');
            continue;
        }
        logger_1.logger.trace({
            count: orders.length,
        }, '👩‍⚖️🏆 Processing pending orders to batch Bid');
        // Prepare arrays to store our final results:
        const executableOrders = [];
        let executableOrdersCount = 0;
        let nonExecutableOrdersCount = 0;
        // Step 1: Map over orders, creating an array of Promises
        for (const order of orders) {
            // Create a "child" logger for each order
            const childLogger = logger_1.logger.child({
                id: order.id,
                sourceNetwork: order.source,
                destinationNetwork: order.destination,
                destinationAsset: order.destinationAsset,
                sourceAsset: order.sourceAsset,
                amount: ethers_1.ethers.utils.formatEther(order.amount),
                maxReward: ethers_1.ethers.utils.formatEther(order.maxReward),
                sourceAccount: order.sourceAccount,
                targetAccount: order.targetAccount,
                txHash: order.txHash,
                enabledNetworks,
            });
            childLogger.trace('🔃🏆 Processing pending order');
            prometheus.profitabilityExecutor.inc({ result: 'total', destinationNetwork: order.destination });
            // Return the promise from handleProcessNewOrderApi
            if (executor.handleProcessNewOrderApi(order)) {
                executableOrders.push(order);
                executableOrdersCount++;
            }
            else {
                nonExecutableOrdersCount++;
            }
        }
        if (nonExecutableOrdersCount > 0) {
            logger_1.logger.info({ count: nonExecutableOrdersCount }, '👩‍⚖️🦑 Pending orders rejected');
        }
        if (executableOrdersCount > 0) {
            logger_1.logger.info({ count: executableOrdersCount }, '👩‍⚖️🏆 Pending orders processed to Bid candidates');
        }
        if (executableOrders.length > 0) {
            // Check if can afford to bid on these orders combined - the checks have been done so far in atomic per-order manner.
            // Use reduce per-asset to calculate the total amount of gas needed for bidding on all orders.
            const combinedAmounts = executableOrders.reduce((acc, order) => {
                const assetDestKey = executor.getAssetOnNetworkKey(order.destination, order.destinationAssetCode);
                if (!acc[assetDestKey]) {
                    acc[assetDestKey] = ethers_1.BigNumber.from(0);
                }
                acc[assetDestKey] = acc[assetDestKey].add(order.amount);
                return acc;
            }, {});
            // Respect the % of max portfolio defined in the arbitrage strategy config
            let allCombinedOkToProceed = true;
            // Iterate over each asset and check if we can afford to bid on these orders combined
            for (const [assetDestKey, amount] of Object.entries(combinedAmounts)) {
                const balance = executor.getBalanceByKey(assetDestKey);
                if (balance.lt(amount.add(ethers_1.ethers.utils.parseEther(config.executor.minBalanceThresholdEth)))) {
                    logger_1.logger.warn({
                        assetDestKey,
                        amount: ethers_1.ethers.utils.formatEther(amount),
                        balance: ethers_1.ethers.utils.formatEther(balance),
                        minBalanceThreshold: ethers_1.ethers.utils.formatEther(ethers_1.ethers.utils.parseEther(config.executor.minBalanceThresholdEth)),
                    }, `🚫 Insufficient balance to bid on ${executableOrdersCount} orders batched. Skipping bidding.`);
                    allCombinedOkToProceed = false;
                    break;
                }
                else {
                    logger_1.logger.trace({
                        assetDestKey,
                        amount: ethers_1.ethers.utils.formatEther(amount),
                        balance: ethers_1.ethers.utils.formatEther(balance),
                        minBalanceThreshold: ethers_1.ethers.utils.formatEther(ethers_1.ethers.utils.parseEther(config.executor.minBalanceThresholdEth)),
                    }, `✅ Sufficient balance to bid on ${executableOrdersCount} orders batched.`);
                }
            }
            if (!allCombinedOkToProceed) {
                continue;
            }
            const gasPriceOnL3 = executor.getGasPriceL3();
            if (gasPriceOnL3.gt(ethers_1.ethers.utils.parseUnits(config.executor.maxL3GasPrice.toString(), 'gwei'))) {
                logger_1.logger.warn({
                    EXECUTOR_MAX_L3_GAS_PRICE: ethers_1.ethers.utils.formatUnits(ethers_1.ethers.utils.parseUnits(config.executor.maxL3GasPrice.toString(), 'gwei'), 'gwei'),
                    latestL3GasPrice: ethers_1.ethers.utils.formatUnits(gasPriceOnL3, 'gwei'),
                }, `🔥⛽️ Gas price on L3 is too high & exceeds ${config.executor.maxL3GasPrice} gwei configured. Skipping bidding.`);
                return ethers_1.BigNumber.from(0);
            }
            if (config.executor.processPendingExecutions.batch) {
                const MAX_BATCH_SIZE = 30;
                for (let i = 0; i < executableOrders.length; i += MAX_BATCH_SIZE) {
                    transmitterL3.transmitBidBatch(executableOrders.slice(i, i + MAX_BATCH_SIZE), executor.getMyAddress(), false, executor.getInsurance());
                }
            }
            else {
                await Promise.all(executableOrders.map((order) => {
                    transmitterL3.transmitBid(order, executor.getMyAddress(), executor.getInsurance());
                }));
            }
        }
    }
}
//# sourceMappingURL=pendingOrders.js.map