// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.18; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "../Lockdrop/extensions/TokenRecovery.sol"; import "../utils/Deployer.sol"; import "./ITemplateFactory.sol"; /** * @notice Implementation of the {ITemplateFactory} interface. */ contract TemplateFactory is ITemplateFactory, ReentrancyGuard, TokenRecovery { using Address for address; using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableSet for EnumerableSet.AddressSet; using SafeCast for uint256; /** * @notice Returns the name of the contract. */ string public constant name = "TemplateFactory"; /** * @notice Returns the Template Factory PUBLIC_ROLE. */ // a PUBLIC_ROLE will allow anybody access bytes32 public constant PUBLIC_ROLE = keccak256("TemplateFactory.PUBLIC_ROLE"); /** * @notice Returns the Template Factory MANAGE_TEMPLATE_ROLE. */ bytes32 public constant MANAGE_TEMPLATE_ROLE = keccak256("TemplateFactory.MANAGE_TEMPLATE_ROLE"); /** * @notice Returns the Template Factory FUNCTION_CALL_ROLE. */ bytes32 public constant FUNCTION_CALL_ROLE = keccak256("TemplateFactory.FUNCTION_CALL_ROLE"); // template address => template information mapping(bytes32 => Template) private _template; mapping(address => DeploymentInfo) private _deployed; /** * @notice Checks if TemplateFactory inherits a given contract interface. * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(ITemplateFactory).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc ITemplateFactory */ function version(bytes32 templateId, uint256 _version) external view override returns (Version memory version_) { Template storage template_ = _template[templateId]; require(template_.versions.length > _version, "TemplateFactory: INVALID_VERSION"); version_ = template_.versions[_version]; } /** * @inheritdoc ITemplateFactory */ function latestVersion(bytes32 templateId) external view override returns (uint256) { require(_template[templateId].versions.length > 0, "TemplateFactory: INVALID_TEMPLATE_ID"); return _template[templateId].versions.length - 1; } /** * @inheritdoc ITemplateFactory */ function templateInstances(bytes32 templateId) external view override returns (address[] memory) { return _template[templateId].instances; } /** * @inheritdoc ITemplateFactory */ function deploymentInfo(address instance) external view override returns (DeploymentInfo memory) { return _deployed[instance]; } /** * @inheritdoc ITemplateFactory */ function deployRole(bytes32 templateId) external view override returns (bytes32) { return _template[templateId].deployRole; } /** * @inheritdoc ITemplateFactory */ function deployedByFactory(address instance) external view override returns (bool) { return _deployed[instance].exists; } /** * @inheritdoc ITemplateFactory */ function uploadTemplate(bytes32 templateId, bytes memory initialPart, uint256 totalParts, address implementation) external override onlyRole(MANAGE_TEMPLATE_ROLE) nonReentrant returns (bool) { require(templateId != bytes32(0), "TemplateFactory: invalid template id"); require(initialPart.length > 0, "TemplateFactory: creation code is empty"); require(totalParts > 0, "TemplateFactory: total parts is zero"); Version memory _version; uint256 _versionId = _template[templateId].versions.length; _version.totalParts = totalParts; _version.creationCode = initialPart; _version.partsUploaded = 1; _version.implementation = implementation; _template[templateId].versions.push(_version); emit TemplateVersionCreated(templateId, _versionId, implementation, _msgSender()); return true; } /** * @inheritdoc ITemplateFactory */ function uploadTemplatePart(bytes32 templateId, uint256 _version, bytes memory part) external override nonReentrant onlyRole(MANAGE_TEMPLATE_ROLE) returns (bool) { require(part.length > 0, "TemplateFactory: creation code is empty"); Version storage vsn = _template[templateId].versions[_version]; require(vsn.partsUploaded < vsn.totalParts, "TemplateFactory: there are no more parts to upload"); vsn.creationCode = abi.encodePacked(vsn.creationCode, part); vsn.partsUploaded++; return true; } /** * @inheritdoc ITemplateFactory */ function updateDeployRole(bytes32 templateId, bytes32 _deployRole) external override onlyRole(MANAGE_TEMPLATE_ROLE) nonReentrant returns (bool) { require(_template[templateId].deployRole != _deployRole, "TemplateFactory: deployRole has not updated"); emit DeployRoleUpdated(templateId, _template[templateId].deployRole, _deployRole, _msgSender()); _template[templateId].deployRole = _deployRole; return true; } /** * @inheritdoc ITemplateFactory */ function disableTemplate(bytes32 templateId) external override onlyRole(MANAGE_TEMPLATE_ROLE) nonReentrant returns (bool) { require(!_template[templateId].disabled, "TemplateFactory: this template is already disabled"); _template[templateId].disabled = true; emit TemplateDisabled(templateId, _msgSender()); return true; } /** * @inheritdoc ITemplateFactory */ function enableTemplate(bytes32 templateId) external override onlyRole(MANAGE_TEMPLATE_ROLE) nonReentrant returns (bool) { require(_template[templateId].disabled, "TemplateFactory: this template is already enabled"); _template[templateId].disabled = false; emit TemplateEnabled(templateId, _msgSender()); return true; } /** * @inheritdoc ITemplateFactory */ function deprecateVersion(bytes32 templateId, uint256 _version) external override onlyRole(MANAGE_TEMPLATE_ROLE) nonReentrant returns (bool) { require( !_template[templateId].versions[_version].deprecated, "TemplateFactory: this template version is already deprecated" ); _template[templateId].versions[_version].deprecated = true; emit TemplateVersionDeprecated(templateId, _version, _msgSender()); return true; } /** * @inheritdoc ITemplateFactory */ function undeprecateVersion(bytes32 templateId, uint256 _version) external override onlyRole(MANAGE_TEMPLATE_ROLE) returns (bool) { require( _template[templateId].versions[_version].deprecated, "TemplateFactory: this template version is not currently deprecated" ); _template[templateId].versions[_version].deprecated = false; emit TemplateVersionUndeprecated(templateId, _version, _msgSender()); return true; } /** * @inheritdoc ITemplateFactory */ function initCodeHash(bytes32 templateId, uint256 _version, bytes memory args) public view override returns (bytes32) { Version storage vsn = _validateTemplateVersion(_template[templateId], _version); return keccak256(_deployCode(vsn.creationCode, args)); } /** * @inheritdoc ITemplateFactory */ function predictDeployAddress(bytes32 templateId, uint256 _version, bytes memory args, bytes32 salt) external view override returns (address) { return Deployer.predictAddress(address(this), initCodeHash(templateId, _version, args), salt); } /** * @inheritdoc ITemplateFactory */ function predictCloneAddress(bytes32 templateId, uint256 _version, bytes32 salt) external view override returns (address) { return Clones.predictDeterministicAddress(_template[templateId].versions[_version].implementation, salt); } /** * @inheritdoc ITemplateFactory */ function deployTemplate( bytes32 templateId, uint256 _version, bytes memory args, bytes[] memory functionCalls, bytes32 salt ) external override nonReentrant returns (address instance) { Template storage tpl = _template[templateId]; bytes memory creationCode = _validateTemplateVersion(tpl, _version).creationCode; bytes memory deployCode = _deployCode(creationCode, args); _validateDeployRole(tpl.deployRole, _msgSender()); _validateDeploySalt(deployCode, salt); instance = Deployer.deploy(deployCode, salt); _registerInstance(templateId, _version, instance, args, functionCalls, false); emit TemplateDeployed(instance, templateId, _version, args, functionCalls, _msgSender()); } /** * @inheritdoc ITemplateFactory */ function cloneTemplate(bytes32 templateId, uint256 _version, bytes[] memory functionCalls, bytes32 salt) external override nonReentrant returns (address instance) { Template storage tpl = _template[templateId]; address implementation = _validateTemplateVersion(tpl, _version).implementation; _validateCloneSalt(implementation, salt); _validateDeployRole(tpl.deployRole, _msgSender()); require(implementation != address(0), "TemplateFactory: implementation or version does not exist"); instance = Clones.cloneDeterministic(implementation, salt); _registerInstance(templateId, _version, instance, "", functionCalls, true); emit TemplateCloned(instance, templateId, _version, functionCalls, _msgSender()); } /** * @inheritdoc ITemplateFactory */ function functionCall(address target, bytes memory data) external override onlyRole(FUNCTION_CALL_ROLE) nonReentrant returns (bytes memory result) { result = target.functionCall(data); emit FunctionCalled(target, data, result, _msgSender()); } function _deployCode(bytes memory creationCode, bytes memory args) private pure returns (bytes memory) { require(creationCode.length > 0, "TemplateFactory: Version does not exist"); return abi.encodePacked(creationCode, args); } function _validateTemplateVersion(Template storage tpl, uint256 _version) private view returns (Version storage vsn) { vsn = tpl.versions[_version]; require(!tpl.disabled, "TemplateFactory: this template has been disabled"); require(!vsn.deprecated, "TemplateFactory: this template version has been deprecated"); require(vsn.totalParts == vsn.partsUploaded, "TemplateFactory: this is an incomplete version. (missing parts)"); } function _validateDeployRole(bytes32 _deployRole, address sender) private view { require( _deployRole == PUBLIC_ROLE || hasRole(_deployRole, sender) || hasRole(MANAGE_TEMPLATE_ROLE, sender), "TemplateFactory: missing required permissions to deploy this template" ); } function _validateDeploySalt(bytes memory deployCode, bytes32 salt) private view { address predictedAddress = Deployer.predictAddress(address(this), keccak256(deployCode), salt); require(!predictedAddress.isContract(), "TemplateFactory: duplicate salt"); } function _validateCloneSalt(address implementation, bytes32 salt) private view { address predictedAddress = _predictCloneAddress(implementation, salt); require(!predictedAddress.isContract(), "TemplateFactory: duplicate salt"); } function _registerInstance( bytes32 templateId, uint256 _version, address instance, bytes memory args, bytes[] memory functionCalls, bool cloned ) private { _template[templateId].instances.push(instance); _template[templateId].versions[_version].instances.push(instance); _deployed[instance] = DeploymentInfo({ exists: true, templateId: templateId, block: block.number.toUint64(), sender: _msgSender(), timestamp: block.timestamp.toUint64(), version: _version, args: args, functionCalls: functionCalls, cloned: cloned }); for (uint256 i; i < functionCalls.length; ++i) { instance.functionCall(functionCalls[i]); } } function _predictCloneAddress(address implementation, bytes32 salt) private view returns (address) { return Clones.predictDeterministicAddress(implementation, salt, address(this)); } }