contract VestedERC20Factory {
        /// -----------------------------------------------------------------------
        /// Library usage
        /// -----------------------------------------------------------------------

        using ClonesWithImmutableArgs for address;

        /// -----------------------------------------------------------------------
        /// Errors
        /// -----------------------------------------------------------------------

        error Error_InvalidTimeRange();

        /// -----------------------------------------------------------------------
        /// Events
        /// -----------------------------------------------------------------------

        event CreateVestedERC20(VestedERC20 vestedERC20);

        /// -----------------------------------------------------------------------
        /// Immutable parameters
        /// -----------------------------------------------------------------------

        /// @notice The VestedERC20 used as the template for all clones created
        VestedERC20 public immutable implementation;

        constructor(VestedERC20 implementation_) {
            implementation = implementation_;
        }

        /// @notice Creates a VestedERC20 contract
        /// @dev Uses a modified minimal proxy contract that stores immutable parameters in code and
        /// passes them in through calldata. See ClonesWithCallData.
        /// @param name The name of the VestedERC20 token
        /// @param symbol The symbol of the VestedERC20 token
        /// @param decimals The number of decimals used by the VestedERC20 token
        /// @param underlying The ERC20 token that is vested
        /// @param startTimestamp The start time of the vest, Unix timestamp in seconds
        /// @param endTimestamp The end time of the vest, must be greater than startTimestamp, Unix timestamp in seconds
        /// @return vestedERC20 The created VestedERC20 contract
        function createVestedERC20(
            bytes32 name,
            bytes32 symbol,
            uint8 decimals,
            address underlying,
            uint64 startTimestamp,
            uint64 endTimestamp
        ) external returns (VestedERC20 vestedERC20) {
            if (endTimestamp <= startTimestamp) {
                revert Error_InvalidTimeRange();
            }
            bytes memory data = abi.encodePacked(
                name,
                symbol,
                decimals,
                underlying,
                startTimestamp,
                endTimestamp
            );
            vestedERC20 = VestedERC20(address(implementation).clone(data));
            emit CreateVestedERC20(vestedERC20);
        }
    }
/// @title VestedERC20Factory
/// @author zefram.eth
/// @notice Factory for deploying VestedERC20 contracts cheaply
function createVestedERC20(
    bytes32 name,
    bytes32 symbol,
    uint8 decimals,
    address underlying,
    uint64 startTimestamp,
    uint64 endTimestamp
) external returns (VestedERC20 vestedERC20) {
    if (endTimestamp <= startTimestamp) {
        revert Error_InvalidTimeRange();
    }
    bytes memory data = abi.encodePacked(
        name,
        symbol,
        decimals,
        underlying,
        startTimestamp,
        endTimestamp
    );
    vestedERC20 = VestedERC20(address(implementation).clone(data));
    emit CreateVestedERC20(vestedERC20);
}

/// @notice Creates a VestedERC20 contract
/// @dev Uses a modified minimal proxy contract that stores immutable parameters in code and
/// passes them in through calldata. See ClonesWithCallData.
/// @param name The name of the VestedERC20 token
/// @param symbol The symbol of the VestedERC20 token
/// @param decimals The number of decimals used by the VestedERC20 token
/// @param underlying The ERC20 token that is vested
/// @param startTimestamp The start time of the vest, Unix timestamp in seconds
/// @param endTimestamp The end time of the vest, must be greater than startTimestamp, Unix timestamp in seconds
/// @return vestedERC20 The created VestedERC20 contract