"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../../../utils/utils");
const constants_1 = require("./constants");
const HubPool_Fixture_1 = require("./fixtures/HubPool.Fixture");
let hubPool, weth, usdc;
let mockSpoke, mockAdapter, identifierWhitelist;
let owner, other;
describe("HubPool Admin functions", function () {
    beforeEach(async function () {
        [owner, other] = await utils_1.ethers.getSigners();
        ({ weth, hubPool, usdc, mockAdapter, mockSpoke, identifierWhitelist } = await (0, HubPool_Fixture_1.hubPoolFixture)());
    });
    it("Can add L1 token to whitelisted lpTokens mapping", async function () {
        (0, utils_1.expect)((await hubPool.callStatic.pooledTokens(weth.address)).lpToken).to.equal(constants_1.zeroAddress);
        await hubPool.enableL1TokenForLiquidityProvision(weth.address);
        const pooledTokenStruct = await hubPool.callStatic.pooledTokens(weth.address);
        (0, utils_1.expect)(pooledTokenStruct.lpToken).to.not.equal(constants_1.zeroAddress);
        (0, utils_1.expect)(pooledTokenStruct.isEnabled).to.equal(true);
        (0, utils_1.expect)(pooledTokenStruct.lastLpFeeUpdate).to.equal(Number(await hubPool.getCurrentTime()));
        const lpToken = await (await (0, utils_1.getContractFactory)("ExpandedERC20", owner)).attach(pooledTokenStruct.lpToken);
        (0, utils_1.expect)(await lpToken.callStatic.symbol()).to.equal("Av2-WETH-LP");
        (0, utils_1.expect)(await lpToken.callStatic.name()).to.equal("Across V2 Wrapped Ether LP Token");
    });
    it("Only owner can enable L1 Tokens for liquidity provision", async function () {
        await (0, utils_1.expect)(hubPool.connect(other).enableL1TokenForLiquidityProvision(weth.address)).to.be.reverted;
    });
    it("Can disable L1 Tokens for liquidity provision", async function () {
        await hubPool.enableL1TokenForLiquidityProvision(weth.address);
        const pooledTokenStruct = await hubPool.callStatic.pooledTokens(weth.address);
        const lpToken = pooledTokenStruct.lpToken;
        const lastLpFeeUpdate = pooledTokenStruct.lastLpFeeUpdate;
        await hubPool.disableL1TokenForLiquidityProvision(weth.address);
        (0, utils_1.expect)((await hubPool.callStatic.pooledTokens(weth.address)).isEnabled).to.equal(false);
        // Can re-enable the L1 token now without creating a new LP token or resetting timestamp.
        await hubPool.enableL1TokenForLiquidityProvision(weth.address);
        (0, utils_1.expect)((await hubPool.callStatic.pooledTokens(weth.address)).lpToken).to.equal(lpToken);
        (0, utils_1.expect)((await hubPool.callStatic.pooledTokens(weth.address)).lastLpFeeUpdate).to.equal(lastLpFeeUpdate);
    });
    it("Only owner can disable L1 Tokens for liquidity provision", async function () {
        await (0, utils_1.expect)(hubPool.connect(other).disableL1TokenForLiquidityProvision(weth.address)).to.be.reverted;
    });
    it("Only owner can set cross chain contract helpers", async function () {
        await (0, utils_1.expect)(hubPool.connect(other).setCrossChainContracts(constants_1.destinationChainId, mockAdapter.address, mockSpoke.address)).to.be.reverted;
    });
    it("Only owner can relay spoke pool admin message", async function () {
        const functionData = mockSpoke.interface.encodeFunctionData("setEnableRoute", [
            weth.address,
            constants_1.destinationChainId,
            false,
        ]);
        await (0, utils_1.expect)(hubPool.connect(other).relaySpokePoolAdminFunction(constants_1.destinationChainId, functionData)).to.be.reverted;
        // Cannot relay admin function if spoke pool is set to zero address or adapter is set to non contract..
        await hubPool.setCrossChainContracts(constants_1.destinationChainId, mockAdapter.address, constants_1.zeroAddress);
        await (0, utils_1.expect)(hubPool.relaySpokePoolAdminFunction(constants_1.destinationChainId, functionData)).to.be.revertedWith("SpokePool not initialized");
        await hubPool.setCrossChainContracts(constants_1.destinationChainId, (0, utils_1.randomAddress)(), mockSpoke.address);
        await (0, utils_1.expect)(hubPool.relaySpokePoolAdminFunction(constants_1.destinationChainId, functionData)).to.be.revertedWith("Adapter not initialized");
        await hubPool.setCrossChainContracts(constants_1.destinationChainId, mockAdapter.address, mockSpoke.address);
        await (0, utils_1.expect)(hubPool.relaySpokePoolAdminFunction(constants_1.destinationChainId, functionData))
            .to.emit(hubPool, "SpokePoolAdminFunctionTriggered")
            .withArgs(constants_1.destinationChainId, functionData);
    });
    it("Only owner can whitelist route for deposits and rebalances", async function () {
        await hubPool.setCrossChainContracts(constants_1.destinationChainId, mockAdapter.address, mockSpoke.address);
        await (0, utils_1.expect)(hubPool.connect(other).setPoolRebalanceRoute(constants_1.destinationChainId, weth.address, usdc.address)).to.be
            .reverted;
        await (0, utils_1.expect)(hubPool.setPoolRebalanceRoute(constants_1.destinationChainId, weth.address, usdc.address))
            .to.emit(hubPool, "SetPoolRebalanceRoute")
            .withArgs(constants_1.destinationChainId, weth.address, usdc.address);
        // Relay deposit route to spoke pool. Check content of messages sent to mock spoke pool.
        await (0, utils_1.expect)(hubPool.connect(other).setDepositRoute(constants_1.originChainId, constants_1.destinationChainId, weth.address, true)).to.be
            .reverted;
        await (0, utils_1.expect)(hubPool.setDepositRoute(constants_1.originChainId, constants_1.destinationChainId, weth.address, true))
            .to.emit(hubPool, "SetEnableDepositRoute")
            .withArgs(constants_1.originChainId, constants_1.destinationChainId, weth.address, true);
        // Disable deposit route on SpokePool right after:
        await hubPool.setDepositRoute(constants_1.originChainId, constants_1.destinationChainId, weth.address, false);
        // Since the mock adapter is delegatecalled, when querying, its address should be the hubPool address.
        const mockAdapterAtHubPool = mockAdapter.attach(hubPool.address);
        const relayMessageEvents = await mockAdapterAtHubPool.queryFilter(mockAdapterAtHubPool.filters.RelayMessageCalled());
        (0, utils_1.expect)(relayMessageEvents[relayMessageEvents.length - 1].args?.message).to.equal(mockSpoke.interface.encodeFunctionData("setEnableRoute", [
            weth.address,
            constants_1.destinationChainId,
            false, // Latest call disabled the route
        ]));
        (0, utils_1.expect)(relayMessageEvents[relayMessageEvents.length - 2].args?.message).to.equal(mockSpoke.interface.encodeFunctionData("setEnableRoute", [
            weth.address,
            constants_1.destinationChainId,
            true, // Second to last call enabled the route
        ]));
    });
    it("Can change the bond token and amount", async function () {
        (0, utils_1.expect)(await hubPool.callStatic.bondToken()).to.equal(weth.address); // Default set in the fixture.
        (0, utils_1.expect)(await hubPool.callStatic.bondAmount()).to.equal(constants_1.bondAmount.add(constants_1.finalFee)); // Default set in the fixture.
        // Set the bond token and amount to 1000 USDC
        const newBondAmount = utils_1.ethers.utils.parseUnits("1000", 6); // set to 1000e6, i.e 1000 USDC.
        await hubPool.setBond(usdc.address, newBondAmount);
        // Can't set bond to 0.
        await (0, utils_1.expect)(hubPool.setBond(usdc.address, "0")).to.be.revertedWith("bond equal to final fee");
        (0, utils_1.expect)(await hubPool.callStatic.bondToken()).to.equal(usdc.address); // New Address.
        (0, utils_1.expect)(await hubPool.callStatic.bondAmount()).to.equal(newBondAmount.add(constants_1.finalFeeUsdc)); // New Bond amount.
    });
    it("Can not change the bond token and amount during a pending refund", async function () {
        await (0, utils_1.seedWallet)(owner, [], weth, constants_1.totalBond);
        await weth.approve(hubPool.address, constants_1.totalBond);
        await hubPool.proposeRootBundle([1, 2, 3], 5, constants_1.mockTreeRoot, constants_1.mockTreeRoot, constants_1.mockSlowRelayRoot);
        await (0, utils_1.expect)(hubPool.setBond(usdc.address, "1")).to.be.revertedWith("Proposal has unclaimed leaves");
    });
    it("Cannot change bond token to unwhitelisted token", async function () {
        await (0, utils_1.expect)(hubPool.setBond((0, utils_1.randomAddress)(), "1")).to.be.revertedWith("Not on whitelist");
    });
    it("Only owner can set bond", async function () {
        await (0, utils_1.expect)(hubPool.connect(other).setBond(usdc.address, "1")).to.be.reverted;
    });
    it("Set identifier", async function () {
        const identifier = (0, utils_1.utf8ToHex)("TEST_ID");
        await identifierWhitelist.addSupportedIdentifier(identifier);
        await hubPool.connect(owner).setIdentifier(identifier);
        (0, utils_1.expect)(await hubPool.identifier()).to.equal(identifier);
    });
    it("Only owner can set identifier", async function () {
        const identifier = (0, utils_1.utf8ToHex)("TEST_ID");
        await identifierWhitelist.addSupportedIdentifier(identifier);
        await (0, utils_1.expect)(hubPool.connect(other).setIdentifier(identifier)).to.be.reverted;
    });
    it("Only whitelisted identifiers allowed", async function () {
        const identifier = (0, utils_1.utf8ToHex)("TEST_ID");
        await (0, utils_1.expect)(hubPool.connect(owner).setIdentifier(identifier)).to.be.revertedWith("Identifier not supported");
    });
    it("Set liveness", async function () {
        const newLiveness = 1000000;
        await hubPool.connect(owner).setLiveness(newLiveness);
        await (0, utils_1.expect)(await hubPool.liveness()).to.equal(newLiveness);
    });
    it("Liveness too short", async function () {
        await (0, utils_1.expect)(hubPool.connect(owner).setLiveness(599)).to.be.revertedWith("Liveness too short");
    });
    it("Only owner can set liveness", async function () {
        await (0, utils_1.expect)(hubPool.connect(other).setLiveness(1000000)).to.be.reverted;
    });
    it("Only owner can pause", async function () {
        await (0, utils_1.expect)(hubPool.connect(other).setPaused(true)).to.be.reverted;
        await (0, utils_1.expect)(hubPool.connect(owner).setPaused(true)).to.emit(hubPool, "Paused").withArgs(true);
    });
    it("Emergency deletion clears the rootBundleProposal", async function () {
        await (0, utils_1.seedWallet)(owner, [], weth, constants_1.totalBond);
        await weth.approve(hubPool.address, constants_1.totalBond);
        await hubPool.proposeRootBundle([1, 2, 3], 5, constants_1.mockTreeRoot, constants_1.mockTreeRoot, constants_1.mockSlowRelayRoot);
        (0, utils_1.expect)(await hubPool.rootBundleProposal()).to.have.property("poolRebalanceRoot", constants_1.mockTreeRoot);
        (0, utils_1.expect)(await hubPool.rootBundleProposal()).to.have.property("unclaimedPoolRebalanceLeafCount", 5);
        await (0, utils_1.expect)(() => hubPool.connect(owner).emergencyDeleteProposal()).to.changeTokenBalances(weth, [owner, hubPool], [constants_1.totalBond, constants_1.totalBond.mul(-1)]);
        (0, utils_1.expect)(await hubPool.rootBundleProposal()).to.have.property("poolRebalanceRoot", utils_1.ethers.utils.hexZeroPad("0x0", 32));
        (0, utils_1.expect)(await hubPool.rootBundleProposal()).to.have.property("unclaimedPoolRebalanceLeafCount", 0);
    });
    it("Emergency deletion can only be called by owner", async function () {
        await (0, utils_1.seedWallet)(owner, [], weth, constants_1.totalBond);
        await weth.approve(hubPool.address, constants_1.totalBond);
        await (0, utils_1.expect)(hubPool.connect(other).emergencyDeleteProposal()).to.be.reverted;
        await (0, utils_1.expect)(hubPool.connect(owner).emergencyDeleteProposal()).to.not.be.reverted;
    });
});
