// File: @openzeppelin/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// File: cache/solpp-generated-contracts/dev-contracts/MultiCall.sol



pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import '../interfaces/IZkLink.sol';

contract MultiCall {
    struct WithdrawToL1Info {
        address owner;
        address token;
        uint128 amount;
        uint16 fastWithdrawFeeRate;
        uint32 accountIdOfNonce;
        uint8 subAccountIdOfNonce;
        uint32 nonce;
        uint256 value;
    }

    struct WithdrawPendingBalanceInfo {
        address payable owner;
        uint16 tokenId;
        uint128 amount;
    }

    struct Result {
        bool success;
        bytes returnData;
    }

    event Call(address target, bytes _calldata, bool success, bytes result);

    function multiStaticCall(
        address[] calldata targets,
        bytes[] calldata calls
    ) external view returns (uint256 blockNumber, Result[] memory returnData) {
        blockNumber = block.number;
        returnData = new Result[](calls.length);
        for (uint i = 0; i < targets.length; i++) {
            (bool success, bytes memory ret) = targets[i].staticcall(calls[i]);
            returnData[i] = Result(success, ret);
        }
    }

    function strictMulticall(
        address[] calldata targets,
        bytes[] calldata calls
    ) external {
        require(targets.length == calls.length, 'targets.length != calls.length');

        for (uint i = 0; i < targets.length; i++) {
            (bool success, bytes memory data) = targets[i].call(calls[i]);
            require(success, string(data));
        }
    }

    function multicall(
        address[] calldata targets,
        bytes[] calldata calls
    ) external {
        require(targets.length == calls.length, 'targets.length != calls.length');

        for (uint i = 0; i < targets.length; i++) {
            (bool success, bytes memory data) = targets[i].call(calls[i]);
            emit Call(targets[i], calls[i], success, data);
        }
    }

    function batchWithdrawToL1(
        IZkLink zkLinkInstance,
        WithdrawToL1Info[] calldata _withdrawDatas
    ) external payable {
        for (uint i; i < _withdrawDatas.length; i++) {
            WithdrawToL1Info memory withdrawInfo = _withdrawDatas[i];
            zkLinkInstance.withdrawToL1{value: withdrawInfo.value}(
                withdrawInfo.owner,
                withdrawInfo.token,
                withdrawInfo.amount,
                withdrawInfo.fastWithdrawFeeRate,
                withdrawInfo.accountIdOfNonce,
                withdrawInfo.subAccountIdOfNonce,
                withdrawInfo.nonce
            );
        }
    }

    function batchWithdrawPendingBalance(
        IZkLink zkLinkInstance,
        WithdrawPendingBalanceInfo[] calldata _withdrawDatas
    ) external {
        for (uint i; i < _withdrawDatas.length; i++) {
            WithdrawPendingBalanceInfo memory withdrawInfo = _withdrawDatas[i];
            zkLinkInstance.withdrawPendingBalance(
                withdrawInfo.owner,
                withdrawInfo.tokenId,
                withdrawInfo.amount
            );
        }
    }
}

// File: cache/solpp-generated-contracts/interfaces/IZkLink.sol



pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title ZkLink interface contract
/// @author zk.link
interface IZkLink {
    /// @notice Return the network governor
    function networkGovernor() external view returns (address);

    /// @notice Deposit ETH to Layer 2 - transfer ether from user into contract, validate it, register deposit
    function depositETH(bytes32 _zkLinkAddress, uint8 _subAccountId) external payable;

    /// @notice Deposit ERC20 token to Layer 2 - transfer ERC20 tokens from user into contract, validate it, register deposit
    function depositERC20(IERC20 _token, uint104 _amount, bytes32 _zkLinkAddress, uint8 _subAccountId, bool _mapping) external;


    /// @notice Receive block confirmation from master chain
    function receiveBlockConfirmation(uint32 blockNumber) external;

    /// @notice Withdraw token to L1 for user by gateway
    /// @param owner User receive token on L1
    /// @param token Token address
    /// @param amount The amount(recovered decimals) of withdraw operation
    /// @param fastWithdrawFeeRate Fast withdraw fee rate taken by acceptor
    /// @param accountIdOfNonce Account that supply nonce, may be different from accountId
    /// @param subAccountIdOfNonce SubAccount that supply nonce
    /// @param nonce SubAccount nonce, used to produce unique accept info
    function withdrawToL1(address owner, address token, uint128 amount, uint16 fastWithdrawFeeRate, uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce) external payable;

    /// @notice  Withdraws tokens from zkLink contract to the owner
    /// @param _owner Address of the tokens owner
    /// @param _tokenId Token id
    /// @param _amount Amount to withdraw to request.
    /// @return The actual withdrawn amount
    /// @dev NOTE: We will call ERC20.transfer(.., _amount), but if according to internal logic of ERC20 token zkLink contract
    /// balance will be decreased by value more then _amount we will try to subtract this value from user pending balance
    function withdrawPendingBalance(address payable _owner, uint16 _tokenId, uint128 _amount) external returns (uint128);
}