1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | // SPDX-License-Identifier: MIT // _____ ____ ______ _____ _______ ____ _______ __ // | __ \ /\ / __ \ | ____/\ / ____|__ __/ __ \| __ \ \ / / // | | | | / \ | | | | | |__ / \ | | | | | | | | |__) \ \_/ / // | | | |/ /\ \| | | | | __/ /\ \| | | | | | | | _ / \ / // | |__| / ____ \ |__| | | | / ____ \ |____ | | | |__| | | \ \ | | // |_____/_/ \_\____/ |_|/_/ \_\_____| |_| \____/|_| \_\ |_| // // // Deployed by createDAO.org - DAO Factory Implementation // GitHub: https://github.com/createdao // // 🌍 This code is free. Like speech. Like people should be. // Use it, learn from it, build with it. Share what you make. // But remember what this is for: not greed, not ego — but freedom, creativity, and unity. // // Inspired by Chaplin's call in The Great Dictator: // “You, the people, have the power — the power to create happiness!” // // So build not for domination, but for decentralization. // Not for walls, but bridges. Not for power, but empowerment. // // Licensed under the MIT License — short, sweet, and to the point. // No restrictions, no delays. Just create. Just be human. ✌️ // — Diornov pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../DAOV1Interfaces.sol"; import "../DAOProxy.sol"; import "../DAOTokenProxy.sol"; import "../DAOTreasuryProxy.sol"; import "../DAOStakingProxy.sol"; import "../core/interfaces/IDAOModule.sol"; /** * @title DAOFactory * @dev This contract is designed to be upgradeable using UUPS proxy pattern. * IMPORTANT: When upgrading, maintain the storage layout to prevent storage collisions. * Storage layout: * - bytes32 slot 0: VERSION (constant, not stored) * - bytes32 slot 1-50: reserved for parent contracts (Initializable, UUPSUpgradeable, OwnableUpgradeable) * - bytes32 slot 51: implementations mapping * - bytes32 slot 52-200: gap for future storage variables */ contract DAOFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable { // Constants (not stored in contract storage) string public constant FACTORY_VERSION = "1.0.1"; /** * @dev Storage layout is critical for upgradeable contracts. * - Never modify the order of existing storage variables * - Only append new variables at the end * - Leave enough gap for future storage variables */ struct CoreImplementation { address daoImplementation; address tokenImplementation; address treasuryImplementation; address stakingImplementation; bytes initializationTemplate; } /** * @dev Checks if a version is the latest registered version * @param versionId The version to check * @return bool True if this is the latest version */ function _isLatestVersion(string memory versionId) internal view returns (bool) { DAOFactoryStorage storage $ = _getStorage(); if ($.availableVersions.length == 0) return false; string memory latestVersion = $.availableVersions[$.availableVersions.length - 1]; return keccak256(abi.encodePacked(versionId)) == keccak256(abi.encodePacked(latestVersion)); } struct ModuleImplementation { address implementation; } /// @custom:storage-location erc7201:dao.factory.storage struct DAOFactoryStorage { // Core DAO implementations mapping(string => CoreImplementation) coreImplementations; // Module implementations by type and version mapping(IDAOModule.ModuleType => mapping(string => ModuleImplementation)) moduleImplementations; // Available versions for core implementations string[] availableVersions; // Track available versions for each module type mapping(IDAOModule.ModuleType => string[]) moduleVersions; } // keccak256(abi.encode(uint256(keccak256("dao.factory.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant STORAGE_LOCATION = 0x9dd4c01fb54f0c00000000000000000000000000000000000000000000000000; function _getStorage() private pure returns (DAOFactoryStorage storage $) { assembly { $.slot := STORAGE_LOCATION } } // Events event DAOCreated( address indexed daoAddress, address indexed tokenAddress, address indexed treasuryAddress, address stakingAddress, string name, string versionId ); event CoreImplementationRegistered(string versionId, address daoImpl); event ModuleImplementationRegistered( IDAOModule.ModuleType indexed moduleType, string versionId, address implementation, string moduleTypeName ); /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize(address _owner) external initializer { require(_owner != address(0), "Zero owner"); __Ownable_init(_owner); __UUPSUpgradeable_init(); } function registerCoreImplementation( string calldata versionId, address daoImpl, address tokenImpl, address treasuryImpl, address stakingImpl, bytes calldata initTemplate ) external onlyOwner { require(daoImpl != address(0), "Zero DAO implementation"); require(tokenImpl != address(0), "Zero token implementation"); require(treasuryImpl != address(0), "Zero treasury implementation"); require(stakingImpl != address(0), "Zero staking implementation"); DAOFactoryStorage storage $ = _getStorage(); require( $.coreImplementations[versionId].daoImplementation == address(0), "Version exists" ); $.coreImplementations[versionId] = CoreImplementation({ daoImplementation: daoImpl, tokenImplementation: tokenImpl, treasuryImplementation: treasuryImpl, stakingImplementation: stakingImpl, initializationTemplate: initTemplate }); $.availableVersions.push(versionId); emit CoreImplementationRegistered(versionId, daoImpl); } function registerModuleImplementation( IDAOModule.ModuleType moduleType, string calldata versionId, address implementation ) external onlyOwner { require(implementation != address(0), "Zero implementation"); DAOFactoryStorage storage $ = _getStorage(); require( $.moduleImplementations[moduleType][versionId].implementation == address(0), "Version exists" ); $.moduleImplementations[moduleType][versionId] = ModuleImplementation({ implementation: implementation }); // Track version for this module type $.moduleVersions[moduleType].push(versionId); emit ModuleImplementationRegistered( moduleType, versionId, implementation, moduleTypeToString(moduleType) ); } function createDAO( string calldata versionId, string memory name, string memory tokenName, string memory tokenSymbol, uint256 initialSupply ) external returns ( address daoAddress, address tokenAddress, address treasuryAddress, address stakingAddress ) { require(_isLatestVersion(versionId), "Only latest version is active"); // Validate token symbol length require(bytes(tokenSymbol).length < 7, "Symbol must be less than 7 chars"); // Validate maximum token supply require(initialSupply <= 999_999_999_999 * 10**18, "Token amount exceeds maximum"); CoreImplementation storage impl = _getStorage().coreImplementations[versionId]; // Deploy token proxy bytes memory tokenInit = abi.encodeWithSelector( IDAOToken.initialize.selector, tokenName, tokenSymbol, initialSupply, msg.sender, // Creator gets initial tokens address(this), // Factory holds rest temporarily address(this) // Factory is initial owner ); DAOTokenProxy tokenProxy = new DAOTokenProxy( impl.tokenImplementation, tokenInit ); tokenAddress = address(tokenProxy); // Deploy treasury proxy (uninitialized) bytes memory emptyInit = ""; DAOTreasuryProxy treasuryProxy = new DAOTreasuryProxy( impl.treasuryImplementation, emptyInit ); treasuryAddress = address(treasuryProxy); // Deploy staking proxy bytes memory stakingInit = abi.encodeWithSelector( IDAOStaking.initialize.selector, tokenAddress ); DAOStakingProxy stakingProxy = new DAOStakingProxy( impl.stakingImplementation, stakingInit ); stakingAddress = address(stakingProxy); // Create and initialize proxy in one step bytes memory daoInit = abi.encodeWithSelector( IDAO.initialize.selector, name, treasuryAddress, stakingAddress, tokenAddress, address(this) ); DAOProxy daoProxy = new DAOProxy(impl.daoImplementation, daoInit); daoAddress = address(daoProxy); // Initialize treasury IDAOTreasury(payable(treasuryAddress)).initialize(daoAddress); // Set staking contract in token IDAOToken(tokenAddress).setStakingContract(stakingAddress); // Transfer token ownership to DAO OwnableUpgradeable(tokenAddress).transferOwnership(daoAddress); // Transfer remaining tokens to treasury uint256 factoryBalance = IERC20(tokenAddress).balanceOf(address(this)); if (factoryBalance > 0) { IERC20(tokenAddress).transfer(treasuryAddress, factoryBalance); } OwnableUpgradeable(daoAddress).transferOwnership(daoAddress); OwnableUpgradeable(stakingAddress).transferOwnership(daoAddress); emit DAOCreated( daoAddress, tokenAddress, treasuryAddress, stakingAddress, name, versionId ); } function getLatestVersion() external view returns (string memory) { DAOFactoryStorage storage $ = _getStorage(); uint256 length = $.availableVersions.length; require(length > 0, "No versions registered"); return $.availableVersions[length - 1]; } function getCoreImplementation( string calldata versionId ) external view returns ( address daoImpl, address tokenImpl, address treasuryImpl, address stakingImpl ) { CoreImplementation storage impl = _getStorage().coreImplementations[versionId]; return ( impl.daoImplementation, impl.tokenImplementation, impl.treasuryImplementation, impl.stakingImplementation ); } function getModuleImplementation( IDAOModule.ModuleType moduleType, string calldata versionId ) external view returns (address implementation) { DAOFactoryStorage storage $ = _getStorage(); string[] storage versions = $.moduleVersions[moduleType]; require(versions.length > 0, "No versions for module"); // Check if this is the latest version string memory latestVersion = versions[versions.length - 1]; require( keccak256(abi.encodePacked(versionId)) == keccak256(abi.encodePacked(latestVersion)), "Only latest version is active" ); return $.moduleImplementations[moduleType][versionId].implementation; } function moduleTypeToString(IDAOModule.ModuleType moduleType) public pure returns (string memory) { if (moduleType == IDAOModule.ModuleType.Presale) return "presale"; if (moduleType == IDAOModule.ModuleType.Vesting) return "vesting"; revert("Unknown module type"); } function getModuleVersions(IDAOModule.ModuleType moduleType) external view returns (string[] memory) { return _getStorage().moduleVersions[moduleType]; } function getLatestModuleVersion(IDAOModule.ModuleType moduleType) external view returns (string memory) { DAOFactoryStorage storage $ = _getStorage(); string[] storage versions = $.moduleVersions[moduleType]; require(versions.length > 0, "No versions for module"); return versions[versions.length - 1]; } function getAvailableVersions() external view returns (string[] memory) { return _getStorage().availableVersions; } function getFactoryVersion() external pure returns (string memory) { return FACTORY_VERSION; } // Required override for UUPS proxy function _authorizeUpgrade( address newImplementation ) internal override onlyOwner {} /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[148] private __gap; } |