// File: contracts/connectors/Connector.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;

import "../libraries/ExecutorLib.sol";
import "../shared/Initializable.sol";

abstract contract Connector is Initializable {
    using ExecutorLib for address;

    address public target;
    address public counterpart;

    /// @dev initialize to keep creation code consistent for create2 deployments
    function initialize(address _target, address _counterpart) public initializer {
        require(_target != address(0), "CNR: Target cannot be zero address");
        require(_counterpart != address(0), "CNR: Counterpart cannot be zero address");

        target = _target;
        counterpart = _counterpart;
    }

    fallback () external payable {
        if (msg.sender == target) {
            _forwardCrossDomainMessage();
        } else {
            _verifyCrossDomainSender();
            target.execute(msg.data, msg.value);
        }
    }

    receive () external payable {
        revert("Do not send ETH to this contract");
    }

    /* ========== Virtual functions ========== */

    function _forwardCrossDomainMessage() internal virtual;

    function _verifyCrossDomainSender() internal virtual;
}
// File: contracts/connectors/PolygonzkConnector.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "../interfaces/polygonzk/messengers/IPolygonZkEVMBridge.sol";
import "./Connector.sol";

contract PolygonzkConnector is Connector {

    uint32 public counterpartNetwork;
    address public messengerAddress;

    function initialize(
        address target,
        address counterpart,
        uint32 _counterpartNetwork,
        address _messengerAddress
    ) external {
        initialize(target, counterpart);

        counterpartNetwork = _counterpartNetwork;
        messengerAddress = _messengerAddress;
    }

    function onMessageReceived(
        address originAddress,
        uint32 originNetwork,
        bytes memory data
    ) external {
        require(originAddress == counterpart, "PLY_ZK_CNR: Origin address does not match counterpart address");
        require(originNetwork == counterpartNetwork, "PLY_ZK_CNR: Origin network does not match counterpart network");
        require(msg.sender == messengerAddress, "PLY_ZK_CNR: Caller is not the messenger");

        uint256 value = 0;
        target.execute(data, value);
    }

    /* ========== Internal functions ========== */

    function _forwardCrossDomainMessage() internal override {
        _forwardCrossDomainMessage(msg.data);
    }

    function _forwardCrossDomainMessage(bytes memory data) internal {
        require(msg.sender == target, "PLY_ZK_CNR: Caller is not the expected sender");

        bool forceUpdateGlobalExitRoot = false;
        IPolygonZkEVMBridge(messengerAddress).bridgeMessage(
            counterpartNetwork,
            counterpart,
            forceUpdateGlobalExitRoot,
            data
        );
    }

    function _verifyCrossDomainSender() internal override {
        revert("BASE_PLY_ZK_CNR: _verifyCrossDomainSender() is handled by onMessageReceived");
    }
}


// File: contracts/interfaces/polygonzk/messengers/IPolygonZkEVMBridge.sol



// SPDX-License-Identifier: AGPL-3.0

pragma solidity 0.6.12;

interface IPolygonZkEVMBridge {
    function bridgeMessage(
        uint32 destinationNetwork,
        address destinationAddress,
        bool forceUpdateGlobalExitRoot,
        bytes calldata metadata
    ) external payable;
}
// File: contracts/libraries/ExecutorLib.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

library ExecutorLib {
    function execute(
        address to,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        (bool success, bytes memory res) = payable(to).call{value: value}(data);
        if (!success) {
            // Bubble up error message
            assembly { revert(add(res,0x20), res) }
        }
        return res;
    }
}
// File: contracts/shared/Initializable.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

contract Initializable {
    bool initialized;

    modifier initializer() {
        require(!initialized, "Initializable: contract is already initialized");
        initialized = true;
        _;
    }
}