"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("./../constants");
const utils_1 = require("../../../../utils/utils");
const HubPool_Fixture_1 = require("../fixtures/HubPool.Fixture");
const MerkleLib_utils_1 = require("../MerkleLib.utils");
const smock_1 = require("@defi-wonderland/smock");
let hubPool, zkSyncAdapter, weth, dai, timer, mockSpoke;
let l2Weth, l2Dai, mainnetWeth;
let owner, dataWorker, liquidityProvider, refundAddress;
let zkSync, zkSyncErc20Bridge;
const zkSyncChainId = 324;
// TODO: Grab the following from relayer/CONTRACT_ADDRESSES dictionary?
const zkSyncAbi = [
    {
        inputs: [
            { internalType: "address", name: "_contractL2", type: "address" },
            { internalType: "uint256", name: "_l2Value", type: "uint256" },
            { internalType: "bytes", name: "_calldata", type: "bytes" },
            { internalType: "uint256", name: "_l2GasLimit", type: "uint256" },
            { internalType: "uint256", name: "_l2GasPerPubdataByteLimit", type: "uint256" },
            { internalType: "bytes[]", name: "_factoryDeps", type: "bytes[]" },
            { internalType: "address", name: "_refundRecipient", type: "address" },
        ],
        name: "requestL2Transaction",
        outputs: [{ internalType: "bytes32", name: "canonicalTxHash", type: "bytes32" }],
        stateMutability: "payable",
        type: "function",
    },
    {
        inputs: [
            { internalType: "uint256", name: "_gasPrice", type: "uint256" },
            { internalType: "uint256", name: "_l2GasLimit", type: "uint256" },
            { internalType: "uint256", name: "_l2GasPerPubdataByteLimit", type: "uint256" },
        ],
        name: "l2TransactionBaseCost",
        outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
        stateMutability: "pure",
        type: "function",
    },
];
const zkSyncErc20BridgeAbi = [
    {
        inputs: [
            { internalType: "address", name: "_l2Receiver", type: "address" },
            { internalType: "address", name: "_l1Token", type: "address" },
            { internalType: "uint256", name: "_amount", type: "uint256" },
            { internalType: "uint256", name: "_l2TxGasLimit", type: "uint256" },
            { internalType: "uint256", name: "_l2TxGasPerPubdataByte", type: "uint256" },
            { internalType: "address", name: "_refundRecipient", type: "address" },
        ],
        name: "deposit",
        outputs: [{ internalType: "bytes32", name: "l2TxHash", type: "bytes32" }],
        stateMutability: "payable",
        type: "function",
    },
];
const l2TransactionBaseCost = (0, utils_1.toWei)("0.0001");
describe("ZkSync Chain Adapter", function () {
    beforeEach(async function () {
        [owner, dataWorker, liquidityProvider, refundAddress] = await utils_1.ethers.getSigners();
        ({ weth, dai, l2Weth, l2Dai, hubPool, mockSpoke, timer } = await (0, HubPool_Fixture_1.hubPoolFixture)());
        await (0, utils_1.seedWallet)(dataWorker, [dai], weth, constants_1.amountToLp);
        await (0, utils_1.seedWallet)(liquidityProvider, [dai], weth, constants_1.amountToLp.mul(10));
        await (0, HubPool_Fixture_1.enableTokensForLP)(owner, hubPool, weth, [weth, dai]);
        await weth.connect(liquidityProvider).approve(hubPool.address, constants_1.amountToLp);
        await hubPool.connect(liquidityProvider).addLiquidity(weth.address, constants_1.amountToLp);
        await weth.connect(dataWorker).approve(hubPool.address, constants_1.bondAmount.mul(10));
        await dai.connect(liquidityProvider).approve(hubPool.address, constants_1.amountToLp);
        await hubPool.connect(liquidityProvider).addLiquidity(dai.address, constants_1.amountToLp);
        await dai.connect(dataWorker).approve(hubPool.address, constants_1.bondAmount.mul(10));
        zkSync = await smock_1.smock.fake(zkSyncAbi, { address: "0x32400084C286CF3E17e7B677ea9583e60a000324" });
        zkSync = await smock_1.smock.fake(zkSyncAbi, { address: "0x32400084C286CF3E17e7B677ea9583e60a000324" });
        zkSync.l2TransactionBaseCost.returns(l2TransactionBaseCost);
        zkSyncErc20Bridge = await smock_1.smock.fake(zkSyncErc20BridgeAbi, {
            address: "0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063",
        });
        zkSyncAdapter = await (await (0, utils_1.getContractFactory)("ZkSync_Adapter", owner)).deploy(weth.address, refundAddress.address);
        // Seed the HubPool some funds so it can send L1->L2 messages.
        await hubPool.connect(liquidityProvider).loadEthForL2Calls({ value: (0, utils_1.toWei)("100000") });
        await hubPool.setCrossChainContracts(zkSyncChainId, zkSyncAdapter.address, mockSpoke.address);
        await hubPool.setPoolRebalanceRoute(zkSyncChainId, weth.address, l2Weth);
        await hubPool.setPoolRebalanceRoute(zkSyncChainId, dai.address, l2Dai);
    });
    it("relayMessage calls spoke pool functions", async function () {
        const newAdmin = (0, utils_1.randomAddress)();
        const functionCallData = mockSpoke.interface.encodeFunctionData("setCrossDomainAdmin", [newAdmin]);
        (0, utils_1.expect)(await hubPool.relaySpokePoolAdminFunction(zkSyncChainId, functionCallData))
            .to.emit(zkSyncAdapter.attach(hubPool.address), "MessageRelayed")
            .withArgs(mockSpoke.address, functionCallData);
        (0, utils_1.expect)(zkSync.requestL2Transaction).to.have.been.calledWith(mockSpoke.address, 0, functionCallData, await zkSyncAdapter.L2_GAS_LIMIT(), await zkSyncAdapter.L1_GAS_TO_L2_GAS_PER_PUB_DATA_LIMIT(), [], refundAddress.address);
        (0, utils_1.expect)(zkSync.requestL2Transaction).to.have.been.calledWithValue(l2TransactionBaseCost);
    });
    it("Correctly calls appropriate bridge functions when making ERC20 cross chain calls", async function () {
        // Create an action that will send an L1->L2 tokens transfer and bundle. For this, create a relayer repayment bundle
        // and check that at it's finalization the L2 bridge contracts are called as expected.
        const { leaves, tree, tokensSendToL2 } = await (0, MerkleLib_utils_1.constructSingleChainTree)(dai.address, 1, zkSyncChainId);
        await hubPool.connect(dataWorker).proposeRootBundle([3117], 1, tree.getHexRoot(), constants_1.mockTreeRoot, constants_1.mockTreeRoot);
        await timer.setCurrentTime(Number(await timer.getCurrentTime()) + constants_1.refundProposalLiveness + 1);
        await hubPool.connect(dataWorker).executeRootBundle(...Object.values(leaves[0]), tree.getHexProof(leaves[0]));
        // The correct functions should have been called on the optimism contracts.
        const expectedErc20L1ToL2BridgeParams = [
            mockSpoke.address,
            dai.address,
            tokensSendToL2,
            await zkSyncAdapter.L2_GAS_LIMIT(),
            await zkSyncAdapter.L1_GAS_TO_L2_GAS_PER_PUB_DATA_LIMIT(),
            refundAddress.address,
        ];
        (0, utils_1.expect)(zkSyncErc20Bridge.deposit).to.have.been.calledWith(...expectedErc20L1ToL2BridgeParams);
        (0, utils_1.expect)(zkSyncErc20Bridge.deposit).to.have.been.calledWithValue(l2TransactionBaseCost);
    });
    it("Correctly unwraps WETH and bridges ETH", async function () {
        const { leaves, tree } = await (0, MerkleLib_utils_1.constructSingleChainTree)(weth.address, 1, zkSyncChainId);
        await hubPool.connect(dataWorker).proposeRootBundle([3117], 1, tree.getHexRoot(), constants_1.mockTreeRoot, constants_1.mockTreeRoot);
        await timer.setCurrentTime(Number(await timer.getCurrentTime()) + constants_1.refundProposalLiveness + 1);
        // Since WETH is used as proposal bond, the bond plus the WETH are debited from the HubPool's balance.
        // The WETH used in the ZKSyncAdapter is withdrawn to ETH and then paid to the zksync mailbox.
        const proposalBond = await hubPool.bondAmount();
        await (0, utils_1.expect)(() => hubPool.connect(dataWorker).executeRootBundle(...Object.values(leaves[0]), tree.getHexProof(leaves[0]))).to.changeTokenBalance(weth, hubPool, leaves[0].netSendAmounts[0].add(proposalBond).mul(-1));
        (0, utils_1.expect)(zkSync.requestL2Transaction).to.have.been.calledWith(mockSpoke.address, leaves[0].netSendAmounts[0].toString(), "0x", await zkSyncAdapter.L2_GAS_LIMIT(), await zkSyncAdapter.L1_GAS_TO_L2_GAS_PER_PUB_DATA_LIMIT(), [], refundAddress.address);
        (0, utils_1.expect)(zkSync.requestL2Transaction).to.have.been.calledWithValue(l2TransactionBaseCost.add(leaves[0].netSendAmounts[0]));
    });
});
