"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
const utils_1 = require("../../../../utils/utils");
const utils_hre_1 = require("../../../../utils/utils.hre");
const HubPool_Fixture_1 = require("../fixtures/HubPool.Fixture");
const MerkleLib_utils_1 = require("../MerkleLib.utils");
const abis_1 = require("../../../../utils/abis");
let hubPool, arbitrumSpokePool, dai, weth;
let l2Weth, l2Dai, l2Usdc, crossDomainAliasAddress;
let owner, relayer, rando, crossDomainAlias;
let l2GatewayRouter, l2CctpTokenMessenger, cctpTokenMinter;
describe("Arbitrum Spoke Pool", function () {
    beforeEach(async function () {
        [owner, relayer, rando] = await utils_1.ethers.getSigners();
        ({ weth, l2Weth, dai, l2Dai, hubPool, l2Usdc } = await (0, HubPool_Fixture_1.hubPoolFixture)());
        // Create an alias for the Owner. Impersonate the account. Crate a signer for it and send it ETH.
        crossDomainAliasAddress = (0, utils_1.avmL1ToL2Alias)(owner.address);
        await utils_hre_1.hre.network.provider.request({ method: "hardhat_impersonateAccount", params: [crossDomainAliasAddress] });
        crossDomainAlias = await utils_1.ethers.getSigner(crossDomainAliasAddress);
        await owner.sendTransaction({ to: crossDomainAliasAddress, value: (0, utils_1.toWei)("1") });
        l2GatewayRouter = await (0, utils_1.createFake)("L2GatewayRouter");
        l2CctpTokenMessenger = await (0, utils_1.createFakeFromABI)(abis_1.CCTPTokenMessengerInterface);
        cctpTokenMinter = await (0, utils_1.createFakeFromABI)(abis_1.CCTPTokenMinterInterface);
        l2CctpTokenMessenger.localMinter.returns(cctpTokenMinter.address);
        cctpTokenMinter.burnLimitsPerMessage.returns((0, utils_1.toWei)("1000000"));
        arbitrumSpokePool = await utils_hre_1.hre.upgrades.deployProxy(await (0, utils_1.getContractFactory)("Arbitrum_SpokePool", owner), [0, l2GatewayRouter.address, owner.address, hubPool.address], {
            kind: "uups",
            unsafeAllow: ["delegatecall"],
            constructorArgs: [l2Weth, 60 * 60, 9 * 60 * 60, l2Usdc, l2CctpTokenMessenger.address],
        });
        await (0, utils_1.seedContract)(arbitrumSpokePool, relayer, [dai], weth, constants_1.amountHeldByPool);
        await arbitrumSpokePool.connect(crossDomainAlias).whitelistToken(l2Dai, dai.address);
    });
    it("Only cross domain owner upgrade logic contract", async function () {
        // TODO: Could also use upgrades.prepareUpgrade but I'm unclear of differences
        const implementation = await utils_hre_1.hre.upgrades.deployImplementation(await (0, utils_1.getContractFactory)("Arbitrum_SpokePool", owner), {
            kind: "uups",
            unsafeAllow: ["delegatecall"],
            constructorArgs: [l2Weth, 60 * 60, 9 * 60 * 60, l2Usdc, l2CctpTokenMessenger.address],
        });
        // upgradeTo fails unless called by cross domain admin
        await (0, utils_1.expect)(arbitrumSpokePool.upgradeTo(implementation)).to.be.revertedWith("ONLY_COUNTERPART_GATEWAY");
        await arbitrumSpokePool.connect(crossDomainAlias).upgradeTo(implementation);
    });
    it("Only cross domain owner can set L2GatewayRouter", async function () {
        await (0, utils_1.expect)(arbitrumSpokePool.setL2GatewayRouter(rando.address)).to.be.reverted;
        await arbitrumSpokePool.connect(crossDomainAlias).setL2GatewayRouter(rando.address);
        (0, utils_1.expect)(await arbitrumSpokePool.l2GatewayRouter()).to.equal(rando.address);
    });
    it("Only cross domain owner can whitelist a token pair", async function () {
        await (0, utils_1.expect)(arbitrumSpokePool.whitelistToken(l2Dai, dai.address)).to.be.reverted;
        await arbitrumSpokePool.connect(crossDomainAlias).whitelistToken(l2Dai, dai.address);
        (0, utils_1.expect)(await arbitrumSpokePool.whitelistedTokens(l2Dai)).to.equal(dai.address);
    });
    it("Only cross domain owner can set the cross domain admin", async function () {
        await (0, utils_1.expect)(arbitrumSpokePool.setCrossDomainAdmin(rando.address)).to.be.reverted;
        await arbitrumSpokePool.connect(crossDomainAlias).setCrossDomainAdmin(rando.address);
        (0, utils_1.expect)(await arbitrumSpokePool.crossDomainAdmin()).to.equal(rando.address);
    });
    it("Only cross domain owner can set the hub pool address", async function () {
        await (0, utils_1.expect)(arbitrumSpokePool.setWithdrawalRecipient(rando.address)).to.be.reverted;
        await arbitrumSpokePool.connect(crossDomainAlias).setWithdrawalRecipient(rando.address);
        (0, utils_1.expect)(await arbitrumSpokePool.withdrawalRecipient()).to.equal(rando.address);
    });
    it("Only cross domain owner can initialize a relayer refund", async function () {
        await (0, utils_1.expect)(arbitrumSpokePool.relayRootBundle(constants_1.mockTreeRoot, constants_1.mockTreeRoot)).to.be.reverted;
        await arbitrumSpokePool.connect(crossDomainAlias).relayRootBundle(constants_1.mockTreeRoot, constants_1.mockTreeRoot);
        (0, utils_1.expect)((await arbitrumSpokePool.rootBundles(0)).slowRelayRoot).to.equal(constants_1.mockTreeRoot);
        (0, utils_1.expect)((await arbitrumSpokePool.rootBundles(0)).relayerRefundRoot).to.equal(constants_1.mockTreeRoot);
    });
    it("Only cross domain owner can delete a relayer refund", async function () {
        await arbitrumSpokePool.connect(crossDomainAlias).relayRootBundle(constants_1.mockTreeRoot, constants_1.mockTreeRoot);
        await (0, utils_1.expect)(arbitrumSpokePool.emergencyDeleteRootBundle(0)).to.be.reverted;
        await (0, utils_1.expect)(arbitrumSpokePool.connect(crossDomainAlias).emergencyDeleteRootBundle(0)).to.not.be.reverted;
        (0, utils_1.expect)((await arbitrumSpokePool.rootBundles(0)).slowRelayRoot).to.equal(utils_1.ethers.utils.hexZeroPad("0x0", 32));
        (0, utils_1.expect)((await arbitrumSpokePool.rootBundles(0)).relayerRefundRoot).to.equal(utils_1.ethers.utils.hexZeroPad("0x0", 32));
    });
    it("Bridge tokens to hub pool correctly calls the Standard L2 Gateway router", async function () {
        const { leaves, tree } = await (0, MerkleLib_utils_1.constructSingleRelayerRefundTree)(l2Dai, await arbitrumSpokePool.callStatic.chainId());
        await arbitrumSpokePool.connect(crossDomainAlias).relayRootBundle(tree.getHexRoot(), constants_1.mockTreeRoot);
        // Reverts if route from arbitrum to mainnet for l2Dai isn't whitelisted.
        await arbitrumSpokePool.connect(crossDomainAlias).whitelistToken(l2Dai, constants_1.zeroAddress);
        await (0, utils_1.expect)(arbitrumSpokePool.executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0]))).to.be.revertedWith("Uninitialized mainnet token");
        await arbitrumSpokePool.connect(crossDomainAlias).whitelistToken(l2Dai, dai.address);
        await arbitrumSpokePool.connect(relayer).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0]));
        // This should have sent tokens back to L1. Check the correct methods on the gateway are correctly called.
        // outboundTransfer is overloaded in the arbitrum gateway. Define the interface to check the method is called.
        const functionKey = "outboundTransfer(address,address,uint256,bytes)";
        (0, utils_1.expect)(l2GatewayRouter[functionKey]).to.have.been.calledOnce;
        (0, utils_1.expect)(l2GatewayRouter[functionKey]).to.have.been.calledWith(dai.address, hubPool.address, constants_1.amountToReturn, "0x");
    });
});
