contract SetTokenCreator {
  using AddressArrayUtils for address[];

  /* ============ Events ============ */

  event SetTokenCreated(address indexed _setToken, address _manager, string _name, string _symbol);

  /* ============ State Variables ============ */

  // Instance of the controller smart contract
  IController public controller;

  /* ============ Functions ============ */

  /**
   * @param _controller          Instance of the controller
   */
  constructor(IController _controller) {
    controller = _controller;
  }

  /**
   * Creates a SetToken smart contract and registers the SetToken with the controller. The SetTokens are composed
   * of positions that are instantiated as DEFAULT (positionState = 0) state.
   *
   * @param _components             List of addresses of components for initial Positions
   * @param _units                  List of units. Each unit is the # of components per 10^18 of a SetToken
   * @param _modules                List of modules to enable. All modules must be approved by the Controller
   * @param _manager                Address of the manager
   * @param _name                   Name of the SetToken
   * @param _symbol                 Symbol of the SetToken
   * @return address                Address of the newly created SetToken
   */
  function create(
    address[] memory _components,
    int256[] memory _units,
    address[] memory _modules,
    address _manager,
    string memory _name,
    string memory _symbol
  ) external returns (address) {
    require(_components.length > 0, "Must have at least 1 component");
    require(_components.length == _units.length, "Component and unit lengths must be the same");
    require(!_components.hasDuplicate(), "Components must not have a duplicate");
    require(_modules.length > 0, "Must have at least 1 module");
    require(_manager != address(0), "Manager must not be empty");

    for (uint256 i = 0; i < _components.length; i++) {
      require(_components[i] != address(0), "Component must not be null address");
      require(_units[i] > 0, "Units must be greater than 0");
    }

    for (uint256 j = 0; j < _modules.length; j++) {
      require(controller.isModule(_modules[j]), "Must be enabled module");
    }

    // Creates a new SetToken instance
    SetToken setToken = new SetToken(
      _components,
      _units,
      _modules,
      controller,
      _manager,
      _name,
      _symbol
    );

    // Registers Set with controller
    controller.addSet(address(setToken));

    emit SetTokenCreated(address(setToken), _manager, _name, _symbol);

    return address(setToken);
  }
}
/**
 * @title SetTokenCreator
 * @author Set Protocol
 *
 * SetTokenCreator is a smart contract used to deploy new SetToken contracts. The SetTokenCreator
 * is a Factory contract that is enabled by the controller to create and register new SetTokens.
 */
function create(
  address[] memory _components,
  int256[] memory _units,
  address[] memory _modules,
  address _manager,
  string memory _name,
  string memory _symbol
) external returns (address) {
  require(_components.length > 0, "Must have at least 1 component");
  require(_components.length == _units.length, "Component and unit lengths must be the same");
  require(!_components.hasDuplicate(), "Components must not have a duplicate");
  require(_modules.length > 0, "Must have at least 1 module");
  require(_manager != address(0), "Manager must not be empty");

  for (uint256 i = 0; i < _components.length; i++) {
    require(_components[i] != address(0), "Component must not be null address");
    require(_units[i] > 0, "Units must be greater than 0");
  }

  for (uint256 j = 0; j < _modules.length; j++) {
    require(controller.isModule(_modules[j]), "Must be enabled module");
  }

  // Creates a new SetToken instance
  SetToken setToken = new SetToken(
    _components,
    _units,
    _modules,
    controller,
    _manager,
    _name,
    _symbol
  );

  // Registers Set with controller
  controller.addSet(address(setToken));

  emit SetTokenCreated(address(setToken), _manager, _name, _symbol);

  return address(setToken);
}

/**
 * Creates a SetToken smart contract and registers the SetToken with the controller. The SetTokens are composed
 * of positions that are instantiated as DEFAULT (positionState = 0) state.
 *
 * @param _components             List of addresses of components for initial Positions
 * @param _units                  List of units. Each unit is the # of components per 10^18 of a SetToken
 * @param _modules                List of modules to enable. All modules must be approved by the Controller
 * @param _manager                Address of the manager
 * @param _name                   Name of the SetToken
 * @param _symbol                 Symbol of the SetToken
 * @return address                Address of the newly created SetToken
 */