import { __assign, __awaiter, __generator } from "tslib";
// Sets up all contracts necessary to build and execute leaves in dataworker merkle roots: relayer refund, slow relay,
import "@nomiclabs/hardhat-ethers";
import "@openzeppelin/hardhat-upgrades";
import "hardhat-deploy";
import hre from "hardhat";
import { getContractFactory } from "../utils/getContractFactory";
import { amountToLp, destinationChainId as defaultDestinationChainId, originChainId as defaultOriginChainId, repaymentChainId, TokenRolesEnum, finalFee, finalFeeUsdc, finalFeeUsdt, bondAmount, randomAddress, } from "../constants";
import { deploySpokePoolWithToken, enableRoutesOnHubPool, createSpyLogger, deployAndConfigureHubPool, deployConfigStore, setupTokensForWallet, getLastBlockTime, } from "../utils";
import * as clients from "../../src/clients";
import { MockConfigStoreClient } from "../mocks";
import { setupUmaEcosystem } from "./UmaEcosystemFixture";
export function setupHubPool(ethers_1, maxRefundPerRelayerRefundLeaf_1, maxL1TokensPerPoolRebalanceLeaf_1) {
    return __awaiter(this, arguments, void 0, function (ethers, maxRefundPerRelayerRefundLeaf, maxL1TokensPerPoolRebalanceLeaf, destinationChainId, originChainId, lookbackForAllChains) {
        var _a, owner, depositor, relayer, dataworker, hubPoolChainId, _b, spokePool_1, erc20_1, _c, spokePool_2, erc20_2, spokePool_3, spokePool_4, spokePoolDeploymentBlocks, _d, _e, _f, _g, testChainIdList, umaEcosystem, _h, hubPool, hubPoolDeploymentBlock, l1Token_1, l1Token_2, _j, spyLogger, spy, configStore, configStoreClient, hubPoolClient, _k, spokePoolClient_1, spokePoolClient_2, spokePoolClient_3, spokePoolClient_4, spokePoolClients, _l, _m, _o, _p;
        var _q, _r;
        var _this = this;
        if (destinationChainId === void 0) { destinationChainId = defaultDestinationChainId; }
        if (originChainId === void 0) { originChainId = defaultOriginChainId; }
        return __generator(this, function (_s) {
            switch (_s.label) {
                case 0: return [4 /*yield*/, ethers.getSigners()];
                case 1:
                    _a = _s.sent(), owner = _a[0], depositor = _a[1], relayer = _a[2], dataworker = _a[3];
                    return [4 /*yield*/, owner.getChainId()];
                case 2:
                    hubPoolChainId = _s.sent();
                    return [4 /*yield*/, deploySpokePoolWithToken(originChainId)];
                case 3:
                    _b = _s.sent(), spokePool_1 = _b.spokePool, erc20_1 = _b.erc20;
                    return [4 /*yield*/, deploySpokePoolWithToken(destinationChainId)];
                case 4:
                    _c = _s.sent(), spokePool_2 = _c.spokePool, erc20_2 = _c.erc20;
                    return [4 /*yield*/, deploySpokePoolWithToken(repaymentChainId)];
                case 5:
                    spokePool_3 = (_s.sent()).spokePool;
                    return [4 /*yield*/, deploySpokePoolWithToken(hubPoolChainId)];
                case 6:
                    spokePool_4 = (_s.sent()).spokePool;
                    _q = {};
                    _d = originChainId;
                    return [4 /*yield*/, spokePool_1.provider.getBlockNumber()];
                case 7:
                    _q[_d] = _s.sent();
                    _e = destinationChainId;
                    return [4 /*yield*/, spokePool_2.provider.getBlockNumber()];
                case 8:
                    _q[_e] = _s.sent();
                    _f = repaymentChainId;
                    return [4 /*yield*/, spokePool_3.provider.getBlockNumber()];
                case 9:
                    _q[_f] = _s.sent();
                    _g = hubPoolChainId;
                    return [4 /*yield*/, spokePool_4.provider.getBlockNumber()];
                case 10:
                    spokePoolDeploymentBlocks = (_q[_g] = _s.sent(),
                        _q);
                    testChainIdList = Object.keys(spokePoolDeploymentBlocks).map(function (_chainId) { return Number(_chainId); });
                    return [4 /*yield*/, setupUmaEcosystem(owner)];
                case 11:
                    umaEcosystem = _s.sent();
                    return [4 /*yield*/, deployAndConfigureHubPool(owner, [
                            { l2ChainId: destinationChainId, spokePool: spokePool_2 },
                            { l2ChainId: originChainId, spokePool: spokePool_1 },
                            // Following spoke pool destinations should not be used in tests but need to be set for dataworker to fetch
                            // spoke pools for those chains in proposeRootBundle
                            { l2ChainId: repaymentChainId, spokePool: spokePool_3 },
                            { l2ChainId: hubPoolChainId, spokePool: spokePool_4 },
                        ], umaEcosystem.finder.address, umaEcosystem.timer.address)];
                case 12:
                    _h = _s.sent(), hubPool = _h.hubPool, hubPoolDeploymentBlock = _h.hubPoolDeploymentBlock, l1Token_1 = _h.l1Token_1, l1Token_2 = _h.l1Token_2;
                    // For each chain, enable routes to both erc20's so that we can fill relays
                    return [4 /*yield*/, enableRoutesOnHubPool(hubPool, [
                            { destinationChainId: originChainId, l1Token: l1Token_1, destinationToken: erc20_1 },
                            { destinationChainId: destinationChainId, l1Token: l1Token_1, destinationToken: erc20_2 },
                            { destinationChainId: originChainId, l1Token: l1Token_2, destinationToken: erc20_2 },
                            { destinationChainId: destinationChainId, l1Token: l1Token_2, destinationToken: erc20_1 },
                            // Need to enable L1 token route to itself on Hub Pool so that hub pool client can look up the L1 token for
                            // its own chain.
                            {
                                destinationChainId: hubPoolChainId,
                                l1Token: l1Token_1,
                                destinationToken: l1Token_1,
                            },
                            // Similar to above, should enable route for repayment chain as well so that config store client can look up
                            // token on that chain.
                            { destinationChainId: repaymentChainId, l1Token: l1Token_1, destinationToken: l1Token_1 },
                        ])];
                case 13:
                    // For each chain, enable routes to both erc20's so that we can fill relays
                    _s.sent();
                    // Set bond currency on hub pool so that roots can be proposed.
                    return [4 /*yield*/, umaEcosystem.collateralWhitelist.addToWhitelist(l1Token_1.address)];
                case 14:
                    // Set bond currency on hub pool so that roots can be proposed.
                    _s.sent();
                    return [4 /*yield*/, umaEcosystem.store.setFinalFee(l1Token_1.address, { rawValue: "0" })];
                case 15:
                    _s.sent();
                    return [4 /*yield*/, hubPool.setBond(l1Token_1.address, "1")];
                case 16:
                    _s.sent(); // We set to 1 Wei since we can't set to 0.
                    // Give dataworker final fee bond to propose roots with:
                    return [4 /*yield*/, setupTokensForWallet(hubPool, dataworker, [l1Token_1], undefined, 100)];
                case 17:
                    // Give dataworker final fee bond to propose roots with:
                    _s.sent();
                    _j = createSpyLogger(), spyLogger = _j.spyLogger, spy = _j.spy;
                    return [4 /*yield*/, deployConfigStore(owner, [l1Token_1, l1Token_2], maxL1TokensPerPoolRebalanceLeaf, maxRefundPerRelayerRefundLeaf)];
                case 18:
                    configStore = (_s.sent()).configStore;
                    configStoreClient = new MockConfigStoreClient(spyLogger, configStore);
                    configStoreClient.setAvailableChains(testChainIdList);
                    return [4 /*yield*/, configStoreClient.update()];
                case 19:
                    _s.sent();
                    hubPoolClient = new clients.HubPoolClient(spyLogger, hubPool, configStoreClient, hubPoolDeploymentBlock, hubPoolChainId);
                    return [4 /*yield*/, _constructSpokePoolClientsWithLookback([spokePool_1, spokePool_2, spokePool_3, spokePool_4], [originChainId, destinationChainId, repaymentChainId, hubPoolChainId], spyLogger, relayer, hubPoolClient, lookbackForAllChains, spokePoolDeploymentBlocks)];
                case 20:
                    _k = _s.sent(), spokePoolClient_1 = _k[0], spokePoolClient_2 = _k[1], spokePoolClient_3 = _k[2], spokePoolClient_4 = _k[3];
                    spokePoolClients = (_r = {},
                        _r[originChainId] = spokePoolClient_1,
                        _r[destinationChainId] = spokePoolClient_2,
                        _r[repaymentChainId] = spokePoolClient_3,
                        _r[hubPoolChainId] = spokePoolClient_4,
                        _r);
                    // Give owner tokens to LP on HubPool with.
                    return [4 /*yield*/, setupTokensForWallet(spokePool_1, owner, [l1Token_1, l1Token_2], undefined, 100)];
                case 21:
                    // Give owner tokens to LP on HubPool with.
                    _s.sent(); // Seed owner to LP.
                    return [4 /*yield*/, l1Token_1.approve(hubPool.address, amountToLp)];
                case 22:
                    _s.sent();
                    return [4 /*yield*/, l1Token_2.approve(hubPool.address, amountToLp)];
                case 23:
                    _s.sent();
                    return [4 /*yield*/, hubPool.addLiquidity(l1Token_1.address, amountToLp)];
                case 24:
                    _s.sent();
                    return [4 /*yield*/, hubPool.addLiquidity(l1Token_2.address, amountToLp)];
                case 25:
                    _s.sent();
                    // Give depositors the tokens they'll deposit into spoke pools:
                    return [4 /*yield*/, setupTokensForWallet(spokePool_1, depositor, [erc20_1, erc20_2], undefined, 10)];
                case 26:
                    // Give depositors the tokens they'll deposit into spoke pools:
                    _s.sent();
                    return [4 /*yield*/, setupTokensForWallet(spokePool_2, depositor, [erc20_2, erc20_1], undefined, 10)];
                case 27:
                    _s.sent();
                    // Give relayers the tokens they'll need to relay on spoke pools:
                    return [4 /*yield*/, setupTokensForWallet(spokePool_1, relayer, [erc20_1, erc20_2, l1Token_1, l1Token_2], undefined, 10)];
                case 28:
                    // Give relayers the tokens they'll need to relay on spoke pools:
                    _s.sent();
                    return [4 /*yield*/, setupTokensForWallet(spokePool_2, relayer, [erc20_1, erc20_2, l1Token_1, l1Token_2], undefined, 10)];
                case 29:
                    _s.sent();
                    _m = (_l = spokePool_1).setCurrentTime;
                    return [4 /*yield*/, getLastBlockTime(spokePool_1.provider)];
                case 30: 
                // Set the spokePool's time to the provider time. This is done to enable the block utility time finder identify a
                // "reasonable" block number based off the block time when looking at quote timestamps.
                return [4 /*yield*/, _m.apply(_l, [_s.sent()])];
                case 31:
                    // Set the spokePool's time to the provider time. This is done to enable the block utility time finder identify a
                    // "reasonable" block number based off the block time when looking at quote timestamps.
                    _s.sent();
                    _p = (_o = spokePool_2).setCurrentTime;
                    return [4 /*yield*/, getLastBlockTime(spokePool_2.provider)];
                case 32: return [4 /*yield*/, _p.apply(_o, [_s.sent()])];
                case 33:
                    _s.sent();
                    return [2 /*return*/, {
                            hubPool: hubPool,
                            spokePool_1: spokePool_1,
                            erc20_1: erc20_1,
                            spokePool_2: spokePool_2,
                            erc20_2: erc20_2,
                            l1Token_1: l1Token_1,
                            l1Token_2: l1Token_2,
                            configStore: configStore,
                            timer: umaEcosystem.timer,
                            spokePoolClient_1: spokePoolClient_1,
                            spokePoolClient_2: spokePoolClient_2,
                            spokePoolClient_3: spokePoolClient_3,
                            spokePoolClient_4: spokePoolClient_4,
                            spokePoolClients: spokePoolClients,
                            configStoreClient: configStoreClient,
                            mockedConfigStoreClient: configStoreClient,
                            hubPoolClient: hubPoolClient,
                            spyLogger: spyLogger,
                            spy: spy,
                            owner: owner,
                            depositor: depositor,
                            relayer: relayer,
                            dataworker: dataworker,
                            updateAllClients: function () { return __awaiter(_this, void 0, void 0, function () {
                                return __generator(this, function (_a) {
                                    switch (_a.label) {
                                        case 0: return [4 /*yield*/, configStoreClient.update()];
                                        case 1:
                                            _a.sent();
                                            return [4 /*yield*/, hubPoolClient.update()];
                                        case 2:
                                            _a.sent();
                                            return [4 /*yield*/, spokePoolClient_1.update()];
                                        case 3:
                                            _a.sent();
                                            return [4 /*yield*/, spokePoolClient_2.update()];
                                        case 4:
                                            _a.sent();
                                            return [4 /*yield*/, spokePoolClient_3.update()];
                                        case 5:
                                            _a.sent();
                                            return [4 /*yield*/, spokePoolClient_4.update()];
                                        case 6:
                                            _a.sent();
                                            return [2 /*return*/];
                                    }
                                });
                            }); },
                        }];
            }
        });
    });
}
function _constructSpokePoolClientsWithLookback(spokePools, spokePoolChains, spyLogger, signer, hubPoolClient, lookbackForAllChains, deploymentBlocks) {
    return __awaiter(this, void 0, void 0, function () {
        var latestBlocks;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, hubPoolClient.update()];
                case 1:
                    _a.sent();
                    return [4 /*yield*/, Promise.all(spokePools.map(function (x) { return x.provider.getBlockNumber(); }))];
                case 2:
                    latestBlocks = _a.sent();
                    return [2 /*return*/, spokePools.map(function (pool, i) {
                            var _a;
                            return new clients.EVMSpokePoolClient(spyLogger, pool.connect(signer), hubPoolClient, spokePoolChains[i], (_a = deploymentBlocks === null || deploymentBlocks === void 0 ? void 0 : deploymentBlocks[spokePoolChains[i]]) !== null && _a !== void 0 ? _a : 0, lookbackForAllChains === undefined ? undefined : { from: latestBlocks[i] - lookbackForAllChains });
                        })];
            }
        });
    });
}
/**
 * Deploys a complete HubPool environment for testing.
 * This is a local implementation that uses our local getContractFactory
 * instead of the one from @across-protocol/contracts.
 */
export function deployHubPool(ethers_1) {
    return __awaiter(this, arguments, void 0, function (ethers, spokePoolName) {
        var _a, signer, crossChainAdmin, parentFixture, weth, usdc, dai, usdt, tokens, lpTokenFactory, hubPool, mockAdapter, mockSpoke, _b, _c, mainnetChainId, mockAdapterMainnet, mockSpokeMainnet, _d, _e, l2UsdtContract, mockTokens;
        if (spokePoolName === void 0) { spokePoolName = "MockSpokePool"; }
        return __generator(this, function (_f) {
            switch (_f.label) {
                case 0: return [4 /*yield*/, ethers.getSigners()];
                case 1:
                    _a = _f.sent(), signer = _a[0], crossChainAdmin = _a[1];
                    return [4 /*yield*/, setupUmaEcosystem(signer)];
                case 2:
                    parentFixture = _f.sent();
                    return [4 /*yield*/, getContractFactory("WETH9", signer)];
                case 3: return [4 /*yield*/, (_f.sent()).deploy()];
                case 4:
                    weth = _f.sent();
                    return [4 /*yield*/, getContractFactory("ExpandedERC20", signer)];
                case 5: return [4 /*yield*/, (_f.sent()).deploy("USD Coin", "USDC", 6)];
                case 6:
                    usdc = _f.sent();
                    return [4 /*yield*/, usdc.addMember(TokenRolesEnum.MINTER, signer.address)];
                case 7:
                    _f.sent();
                    return [4 /*yield*/, getContractFactory("ExpandedERC20", signer)];
                case 8: return [4 /*yield*/, (_f.sent()).deploy("DAI Stablecoin", "DAI", 18)];
                case 9:
                    dai = _f.sent();
                    return [4 /*yield*/, dai.addMember(TokenRolesEnum.MINTER, signer.address)];
                case 10:
                    _f.sent();
                    return [4 /*yield*/, getContractFactory("ExpandedERC20", signer)];
                case 11: return [4 /*yield*/, (_f.sent()).deploy("USDT Stablecoin", "USDT", 6)];
                case 12:
                    usdt = _f.sent();
                    return [4 /*yield*/, usdt.addMember(TokenRolesEnum.MINTER, signer.address)];
                case 13:
                    _f.sent();
                    tokens = { weth: weth, usdc: usdc, dai: dai, usdt: usdt };
                    // Set the above currencies as approved in the UMA collateralWhitelist
                    return [4 /*yield*/, parentFixture.collateralWhitelist.addToWhitelist(weth.address)];
                case 14:
                    // Set the above currencies as approved in the UMA collateralWhitelist
                    _f.sent();
                    return [4 /*yield*/, parentFixture.collateralWhitelist.addToWhitelist(usdc.address)];
                case 15:
                    _f.sent();
                    return [4 /*yield*/, parentFixture.collateralWhitelist.addToWhitelist(dai.address)];
                case 16:
                    _f.sent();
                    return [4 /*yield*/, parentFixture.collateralWhitelist.addToWhitelist(usdt.address)];
                case 17:
                    _f.sent();
                    // Set the finalFee for all the new tokens
                    return [4 /*yield*/, parentFixture.store.setFinalFee(weth.address, { rawValue: finalFee })];
                case 18:
                    // Set the finalFee for all the new tokens
                    _f.sent();
                    return [4 /*yield*/, parentFixture.store.setFinalFee(usdc.address, { rawValue: finalFeeUsdc })];
                case 19:
                    _f.sent();
                    return [4 /*yield*/, parentFixture.store.setFinalFee(dai.address, { rawValue: finalFee })];
                case 20:
                    _f.sent();
                    return [4 /*yield*/, parentFixture.store.setFinalFee(usdt.address, { rawValue: finalFeeUsdt })];
                case 21:
                    _f.sent();
                    return [4 /*yield*/, getContractFactory("LpTokenFactory", signer)];
                case 22: return [4 /*yield*/, (_f.sent()).deploy()];
                case 23:
                    lpTokenFactory = _f.sent();
                    return [4 /*yield*/, getContractFactory("HubPool", signer)];
                case 24: return [4 /*yield*/, (_f.sent()).deploy(lpTokenFactory.address, parentFixture.finder.address, weth.address, parentFixture.timer.address)];
                case 25:
                    hubPool = _f.sent();
                    return [4 /*yield*/, hubPool.setBond(weth.address, bondAmount)];
                case 26:
                    _f.sent();
                    return [4 /*yield*/, hubPool.setLiveness(7200)];
                case 27:
                    _f.sent(); // refundProposalLiveness
                    return [4 /*yield*/, getContractFactory("Mock_Adapter", signer)];
                case 28: return [4 /*yield*/, (_f.sent()).deploy()];
                case 29:
                    mockAdapter = _f.sent();
                    _c = (_b = hre.upgrades).deployProxy;
                    return [4 /*yield*/, getContractFactory(spokePoolName, signer)];
                case 30: return [4 /*yield*/, _c.apply(_b, [_f.sent(), [0, crossChainAdmin.address, hubPool.address],
                        { kind: "uups", unsafeAllow: ["delegatecall"], constructorArgs: [weth.address] }])];
                case 31:
                    mockSpoke = _f.sent();
                    return [4 /*yield*/, hubPool.setCrossChainContracts(repaymentChainId, mockAdapter.address, mockSpoke.address)];
                case 32:
                    _f.sent();
                    return [4 /*yield*/, hubPool.setCrossChainContracts(defaultOriginChainId, mockAdapter.address, mockSpoke.address)];
                case 33:
                    _f.sent();
                    return [4 /*yield*/, hre.getChainId()];
                case 34:
                    mainnetChainId = _f.sent();
                    return [4 /*yield*/, getContractFactory("Mock_Adapter", signer)];
                case 35: return [4 /*yield*/, (_f.sent()).deploy()];
                case 36:
                    mockAdapterMainnet = _f.sent();
                    _e = (_d = hre.upgrades).deployProxy;
                    return [4 /*yield*/, getContractFactory(spokePoolName, signer)];
                case 37: return [4 /*yield*/, _e.apply(_d, [_f.sent(), [0, crossChainAdmin.address, hubPool.address],
                        { kind: "uups", unsafeAllow: ["delegatecall"], constructorArgs: [weth.address] }])];
                case 38:
                    mockSpokeMainnet = _f.sent();
                    return [4 /*yield*/, hubPool.setCrossChainContracts(mainnetChainId, mockAdapterMainnet.address, mockSpokeMainnet.address)];
                case 39:
                    _f.sent();
                    return [4 /*yield*/, getContractFactory("ExpandedERC20", signer)];
                case 40: return [4 /*yield*/, (_f.sent()).deploy("USDT Stablecoin", "USDT", 6)];
                case 41:
                    l2UsdtContract = _f.sent();
                    return [4 /*yield*/, l2UsdtContract.addMember(TokenRolesEnum.MINTER, signer.address)];
                case 42:
                    _f.sent();
                    mockTokens = {
                        l2Weth: randomAddress(),
                        l2Dai: randomAddress(),
                        l2Usdc: randomAddress(),
                        l2Usdt: l2UsdtContract.address,
                    };
                    // Whitelist pool rebalance routes but don't relay any messages to SpokePool
                    return [4 /*yield*/, hubPool.setPoolRebalanceRoute(repaymentChainId, weth.address, mockTokens.l2Weth)];
                case 43:
                    // Whitelist pool rebalance routes but don't relay any messages to SpokePool
                    _f.sent();
                    return [4 /*yield*/, hubPool.setPoolRebalanceRoute(repaymentChainId, dai.address, mockTokens.l2Dai)];
                case 44:
                    _f.sent();
                    return [4 /*yield*/, hubPool.setPoolRebalanceRoute(repaymentChainId, usdc.address, mockTokens.l2Usdc)];
                case 45:
                    _f.sent();
                    return [4 /*yield*/, hubPool.setPoolRebalanceRoute(repaymentChainId, usdt.address, mockTokens.l2Usdt)];
                case 46:
                    _f.sent();
                    return [2 /*return*/, __assign(__assign(__assign(__assign(__assign({}, tokens), { l2UsdtContract: l2UsdtContract }), mockTokens), { hubPool: hubPool, mockAdapter: mockAdapter, mockSpoke: mockSpoke, crossChainAdmin: crossChainAdmin }), parentFixture)];
            }
        });
    });
}
/**
 * Creates a hub pool fixture using hardhat-deploy's createFixture.
 */
export var hubPoolFixture = hre.deployments.createFixture(function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
    var ethers = _b.ethers;
    return __generator(this, function (_c) {
        switch (_c.label) {
            case 0: return [4 /*yield*/, deployHubPool(ethers)];
            case 1: return [2 /*return*/, _c.sent()];
        }
    });
}); });
/**
 * Enables tokens for liquidity provision on the hub pool.
 */
export function enableTokensForLP(owner, hubPool, _weth, tokens) {
    return __awaiter(this, void 0, void 0, function () {
        var lpTokens, _i, tokens_1, token, pooledTokens, _a, _b;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    lpTokens = [];
                    _i = 0, tokens_1 = tokens;
                    _c.label = 1;
                case 1:
                    if (!(_i < tokens_1.length)) return [3 /*break*/, 7];
                    token = tokens_1[_i];
                    return [4 /*yield*/, hubPool.enableL1TokenForLiquidityProvision(token.address)];
                case 2:
                    _c.sent();
                    return [4 /*yield*/, hubPool.callStatic.pooledTokens(token.address)];
                case 3:
                    pooledTokens = _c.sent();
                    _b = (_a = lpTokens).push;
                    return [4 /*yield*/, getContractFactory("ExpandedERC20", owner)];
                case 4: return [4 /*yield*/, (_c.sent()).attach(pooledTokens.lpToken)];
                case 5:
                    _b.apply(_a, [_c.sent()]);
                    _c.label = 6;
                case 6:
                    _i++;
                    return [3 /*break*/, 1];
                case 7: return [2 /*return*/, lpTokens];
            }
        });
    });
}
//# sourceMappingURL=HubPool.Fixture.js.map