abstract contract TokenGateway is ITokenGateway {
    using Address for address;
    address public counterpartGateway;
    address public router;

    modifier onlyCounterpartGateway() virtual {
        require(isCounterpartGateway(msg.sender), "ONLY_COUNTERPART_GATEWAY");
        _;
    }

    function _initialize(address _counterpartGateway, address _router) internal virtual {
        require(_counterpartGateway != address(0), "INVALID_COUNTERPART");
        require(counterpartGateway == address(0), "ALREADY_INIT");
        counterpartGateway = _counterpartGateway;
        router = _router;
    }

    function isRouter(address _target) internal view virtual returns (bool isTargetRouter) {
        return _target == router;
    }

    function isCounterpartGateway(address _target) internal view virtual returns (bool) {
        return _target == counterpartGateway;
    }

    /**
     * @notice Calculate the address used when bridging an ERC20 token
     * @dev this always returns the same as the L1 oracle, but may be out of date.
     * For example, a custom token may have been registered but not deploy or the contract self destructed.
     * @param l1ERC20 address of L1 token
     * @return L2 address of a bridged ERC20 token
     */
    function calculateL2TokenAddress(address l1ERC20)
        external
        view
        virtual
        override
        returns (address)
    {
        // will revert if not called by router
        return _calculateL2TokenAddress(l1ERC20);
    }

    /**
     * @notice Calculate the address used when bridging an ERC20 token
     * @dev this always returns the same as the L1 oracle, but may be out of date.
     * For example, a custom token may have been registered but not deploy or the contract self destructed.
     * @param l1ERC20 address of L1 token
     * @return L2 address of a bridged ERC20 token
     */
    function _calculateL2TokenAddress(address l1ERC20) internal view virtual returns (address);

    function outboundTransfer(
        address _token,
        address _to,
        uint256 _amount,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        bytes calldata _data
    ) external payable virtual override returns (bytes memory);

    function getOutboundCalldata(
        address _token,
        address _from,
        address _to,
        uint256 _amount,
        bytes memory _data
    ) public view virtual returns (bytes memory);

    function finalizeInboundTransfer(
        address _token,
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external payable virtual override returns (bytes memory);
}
function calculateL2TokenAddress(address l1ERC20)
    external
    view
    virtual
    override
    returns (address)
{
    // will revert if not called by router
    return _calculateL2TokenAddress(l1ERC20);
}

/**
 * @notice Calculate the address used when bridging an ERC20 token
 * @dev this always returns the same as the L1 oracle, but may be out of date.
 * For example, a custom token may have been registered but not deploy or the contract self destructed.
 * @param l1ERC20 address of L1 token
 * @return L2 address of a bridged ERC20 token
 */