contract ZksTreasury is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;
    event DepositETH(address indexed depositer, uint amount);

    ZksCore public zksCoreAddress;
    address public receiverLayer2;
    address public depositWorker;

    modifier onlyDepositWorker() {
        require(depositWorker == msg.sender, "not deposit worker");
        _;
    }

    constructor(address receiverL2, address depositWorkerAddr, address zksCoreAddr){
        receiverLayer2 = receiverL2;
        depositWorker = depositWorkerAddr;
        zksCoreAddress = ZksCore(zksCoreAddr);
    }

    receive() external payable {
        emit DepositETH(msg.sender, msg.value);
    }

    // deposit eth locked in this contract to layer2
    function depositEthToZksCore(uint amount) external onlyDepositWorker {
        zksCoreAddress.depositETH{value : amount}(receiverLayer2);
    }

    // deposit erc20 locked in this contract to layer2
    function depositErc20ToZksCore(
        address[] calldata tokenAddresses,
        uint104[] calldata amounts
    )
    external
    nonReentrant
    onlyDepositWorker
    {
        require(tokenAddresses.length == amounts.length, "unmatched length");
        for (uint i = 0; i < tokenAddresses.length; ++i) {
            zksCoreAddress.depositERC20(IERC20(tokenAddresses[i]), amounts[i], receiverLayer2);
        }
    }

    // receiverLayer2 setter
    function setReceiverLayer2(address newReceiverLayer2) external onlyOwner {
        receiverLayer2 = newReceiverLayer2;
    }

    // zksCoreAddress setter
    function setZksCoreAddress(address newZksCoreAddress) external onlyOwner {
        zksCoreAddress = ZksCore(newZksCoreAddress);
    }

    // depositWorker setter
    function setDepositWorker(address newDepositWorker) external onlyOwner {
        depositWorker = newDepositWorker;
    }

    // give erc20 approval to Zks core contract
    function approveToZksCore(
        address[] calldata tokenAddresses,
        uint[] calldata allowances
    )
    external
    nonReentrant
    onlyOwner
    {
        require(tokenAddresses.length == allowances.length, "unmatched length");
        for (uint i = 0; i < tokenAddresses.length; ++i) {
            IERC20(tokenAddresses[i]).safeApprove(address(zksCoreAddress), allowances[i]);
        }
    }

    // for emergency
    function emergencyWithdraw(address tokenAddress, uint amount) external onlyOwner nonReentrant {
        if (tokenAddress != address(0)) {
            // withdraw ERC20
            IERC20(tokenAddress).safeTransfer(msg.sender, amount);
            return;
        }

        // withdraw eth
        payable(msg.sender).transfer(amount);
    }
}
function emergencyWithdraw(address tokenAddress, uint amount) external onlyOwner nonReentrant {
    if (tokenAddress != address(0)) {
        // withdraw ERC20
        IERC20(tokenAddress).safeTransfer(msg.sender, amount);
        return;
    }

    // withdraw eth
    payable(msg.sender).transfer(amount);
}

// for emergency