"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.constructPoolRebalanceLeaves = exports.updateRunningBalanceForDeposit = exports.addLastRunningBalance = exports.updateRunningBalance = exports.getRunningBalanceForL1Token = exports.getNetSendAmountForL1Token = exports.computeDesiredTransferAmountToSpoke = exports.isChainDisabled = exports.getWidestPossibleExpectedBlockRange = void 0;
var tslib_1 = require("tslib");
var SpokePoolClient_1 = require("../../SpokePoolClient");
var utils_1 = require("../../../utils");
var svm_1 = require("../../../arch/svm");
var assert_1 = tslib_1.__importDefault(require("assert"));
function getWidestPossibleExpectedBlockRange(chainIds, spokeClients, endBlockBuffers, clients, latestMainnetBlock, enabledChains, optimistic) {
    if (optimistic === void 0) { optimistic = false; }
    return tslib_1.__awaiter(this, void 0, void 0, function () {
        var resolveEndBlock, resolveSVMEndBlock, latestPossibleBundleEndBlockNumbers;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    resolveEndBlock = function (chainId, idx) {
                        return Math.max(spokeClients[chainId].latestHeightSearched - endBlockBuffers[idx], 0);
                    };
                    resolveSVMEndBlock = function (chainId, idx) {
                        var spokePoolClient = spokeClients[chainId];
                        (0, assert_1.default)((0, SpokePoolClient_1.isSVMSpokePoolClient)(spokePoolClient));
                        var maxSlot = resolveEndBlock(chainId, idx);
                        return (0, svm_1.getLatestFinalizedSlotWithBlock)(spokePoolClient.svmEventsClient.getRpc(), BigInt(maxSlot));
                    };
                    return [4, Promise.all(chainIds.map(function (chainId, idx) {
                            if (!enabledChains.includes(chainId) || !(0, utils_1.isDefined)(spokeClients[chainId])) {
                                return -1;
                            }
                            if ((0, utils_1.chainIsEvm)(chainId)) {
                                return Promise.resolve(resolveEndBlock(chainId, idx));
                            }
                            if ((0, utils_1.chainIsSvm)(chainId)) {
                                return resolveSVMEndBlock(chainId, idx);
                            }
                            (0, assert_1.default)(false, "Unsupported chainId: ".concat(chainId));
                        }))];
                case 1:
                    latestPossibleBundleEndBlockNumbers = _a.sent();
                    return [2, chainIds.map(function (chainId, index) {
                            var lastEndBlockForChain = clients.hubPoolClient.getLatestBundleEndBlockForChain(chainIds, latestMainnetBlock, chainId);
                            if (!enabledChains.includes(chainId)) {
                                return [lastEndBlockForChain, lastEndBlockForChain];
                            }
                            if (lastEndBlockForChain >= latestPossibleBundleEndBlockNumbers[index]) {
                                return [lastEndBlockForChain, lastEndBlockForChain];
                            }
                            return [
                                optimistic
                                    ? clients.hubPoolClient.getOptimisticBundleStartBlockNumber(chainIds, latestMainnetBlock, chainId)
                                    : clients.hubPoolClient.getNextBundleStartBlockNumber(chainIds, latestMainnetBlock, chainId),
                                latestPossibleBundleEndBlockNumbers[index],
                            ];
                        })];
            }
        });
    });
}
exports.getWidestPossibleExpectedBlockRange = getWidestPossibleExpectedBlockRange;
function isChainDisabled(blockRangeForChain) {
    return blockRangeForChain[0] === blockRangeForChain[1];
}
exports.isChainDisabled = isChainDisabled;
function computeDesiredTransferAmountToSpoke(runningBalance, spokePoolTargetBalance) {
    if (runningBalance.gte(0)) {
        return runningBalance;
    }
    if (runningBalance.abs().lt(spokePoolTargetBalance.threshold)) {
        return utils_1.bnZero;
    }
    var transferSize = runningBalance.abs().sub(spokePoolTargetBalance.target);
    if (transferSize.lt(0)) {
        return utils_1.bnZero;
    }
    return transferSize.mul(-1);
}
exports.computeDesiredTransferAmountToSpoke = computeDesiredTransferAmountToSpoke;
function getNetSendAmountForL1Token(spokePoolTargetBalance, runningBalance) {
    return computeDesiredTransferAmountToSpoke(runningBalance, spokePoolTargetBalance);
}
exports.getNetSendAmountForL1Token = getNetSendAmountForL1Token;
function getRunningBalanceForL1Token(spokePoolTargetBalance, runningBalance) {
    var desiredTransferAmount = computeDesiredTransferAmountToSpoke(runningBalance, spokePoolTargetBalance);
    return runningBalance.sub(desiredTransferAmount);
}
exports.getRunningBalanceForL1Token = getRunningBalanceForL1Token;
function updateRunningBalance(runningBalances, l2ChainId, l1Token, updateAmount) {
    if (!runningBalances[l2ChainId]) {
        runningBalances[l2ChainId] = {};
    }
    var runningBalance = runningBalances[l2ChainId][l1Token];
    if (runningBalance) {
        runningBalances[l2ChainId][l1Token] = runningBalance.add(updateAmount);
    }
    else {
        runningBalances[l2ChainId][l1Token] = updateAmount;
    }
}
exports.updateRunningBalance = updateRunningBalance;
function addLastRunningBalance(latestMainnetBlock, runningBalances, hubPoolClient) {
    Object.keys(runningBalances).forEach(function (repaymentChainId) {
        Object.keys(runningBalances[Number(repaymentChainId)]).forEach(function (l1TokenAddress) {
            var runningBalance = hubPoolClient.getRunningBalanceBeforeBlockForChain(latestMainnetBlock, Number(repaymentChainId), utils_1.EvmAddress.from(l1TokenAddress)).runningBalance;
            if (!runningBalance.eq(utils_1.bnZero)) {
                updateRunningBalance(runningBalances, Number(repaymentChainId), l1TokenAddress, runningBalance);
            }
        });
    });
}
exports.addLastRunningBalance = addLastRunningBalance;
function updateRunningBalanceForDeposit(runningBalances, hubPoolClient, deposit, updateAmount, mainnetBundleEndBlock) {
    var l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(deposit.inputToken, deposit.originChainId, mainnetBundleEndBlock);
    updateRunningBalance(runningBalances, deposit.originChainId, l1TokenCounterpart.toEvmAddress(), updateAmount);
}
exports.updateRunningBalanceForDeposit = updateRunningBalanceForDeposit;
function constructPoolRebalanceLeaves(latestMainnetBlock, runningBalances, realizedLpFees, chainsWithRefundsOnly, configStoreClient, maxL1TokenCount) {
    (0, assert_1.default)(chainsWithRefundsOnly.every(function (chainId) { return runningBalances[chainId] === undefined; }) &&
        chainsWithRefundsOnly.every(function (chainId) { return realizedLpFees[chainId] === undefined; }), "Refund-only chains should not have running balances or realized LP fees.");
    var leaves = [];
    Object.keys(runningBalances)
        .map(function (chainId) { return Number(chainId); })
        .concat(chainsWithRefundsOnly)
        .sort(function (chainIdA, chainIdB) { return chainIdA - chainIdB; })
        .map(function (chainId) {
        if (chainsWithRefundsOnly.includes(chainId)) {
            leaves.push({
                chainId: chainId,
                bundleLpFees: [],
                netSendAmounts: [],
                runningBalances: [],
                groupIndex: 0,
                leafId: leaves.length,
                l1Tokens: [],
            });
            return;
        }
        var sortedL1Tokens = Object.keys(runningBalances[chainId]).sort(function (addressA, addressB) {
            return (0, utils_1.compareAddresses)(addressA, addressB);
        });
        var groupIndexForChainId = 0;
        var maxL1TokensPerLeaf = maxL1TokenCount || configStoreClient.getMaxRefundCountForRelayerRefundLeafForBlock(latestMainnetBlock);
        var _loop_1 = function (i) {
            var l1TokensToIncludeInThisLeaf = sortedL1Tokens.slice(i, i + maxL1TokensPerLeaf);
            var spokeTargetBalances = l1TokensToIncludeInThisLeaf.map(function (l1Token) {
                return configStoreClient.getSpokeTargetBalancesForBlock(l1Token, chainId, latestMainnetBlock);
            });
            var leafBundleLpFees = l1TokensToIncludeInThisLeaf.map(function (l1Token) { var _a, _b; return (_b = (_a = realizedLpFees[chainId]) === null || _a === void 0 ? void 0 : _a[l1Token]) !== null && _b !== void 0 ? _b : utils_1.bnZero; });
            var leafNetSendAmounts = l1TokensToIncludeInThisLeaf.map(function (l1Token, index) {
                return runningBalances[chainId] && runningBalances[chainId][l1Token]
                    ? getNetSendAmountForL1Token(spokeTargetBalances[index], runningBalances[chainId][l1Token])
                    : utils_1.bnZero;
            });
            var leafRunningBalances = l1TokensToIncludeInThisLeaf.map(function (l1Token, index) {
                var _a;
                return ((_a = runningBalances[chainId]) === null || _a === void 0 ? void 0 : _a[l1Token])
                    ? getRunningBalanceForL1Token(spokeTargetBalances[index], runningBalances[chainId][l1Token])
                    : utils_1.bnZero;
            });
            leaves.push({
                chainId: chainId,
                bundleLpFees: leafBundleLpFees,
                netSendAmounts: leafNetSendAmounts,
                runningBalances: leafRunningBalances,
                groupIndex: groupIndexForChainId++,
                leafId: leaves.length,
                l1Tokens: l1TokensToIncludeInThisLeaf.map(function (l1TokenAddr) { return utils_1.EvmAddress.from(l1TokenAddr); }),
            });
        };
        for (var i = 0; i < sortedL1Tokens.length; i += maxL1TokensPerLeaf) {
            _loop_1(i);
        }
    });
    return leaves;
}
exports.constructPoolRebalanceLeaves = constructPoolRebalanceLeaves;
//# sourceMappingURL=PoolRebalanceUtils.js.map