// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "./interfaces/ITokenFactory138.sol"; import "./interfaces/IeMoneyToken.sol"; import "./interfaces/IPolicyManager.sol"; import "./eMoneyToken.sol"; import "./errors/FactoryErrors.sol"; import "./errors/RegistryErrors.sol"; /** * @title TokenFactory138 * @notice Factory for deploying new eMoneyToken instances as UUPS upgradeable proxies * @dev Deploys ERC1967Proxy instances pointing to a shared implementation contract. * Each token is configured with its issuer, lien mode, bridge settings, and registered by code hash. */ contract TokenFactory138 is ITokenFactory138, AccessControl { bytes32 public constant TOKEN_DEPLOYER_ROLE = keccak256("TOKEN_DEPLOYER_ROLE"); address public immutable implementation; address public immutable policyManager; address public immutable debtRegistry; address public immutable complianceRegistry; mapping(bytes32 => address) private _tokensByCodeHash; /** * @notice Initializes the factory with registry and implementation addresses * @param admin Address that will receive DEFAULT_ADMIN_ROLE * @param implementation_ Address of the eMoneyToken implementation contract (used for all proxies) * @param policyManager_ Address of PolicyManager contract * @param debtRegistry_ Address of DebtRegistry contract * @param complianceRegistry_ Address of ComplianceRegistry contract */ constructor( address admin, address implementation_, address policyManager_, address debtRegistry_, address complianceRegistry_ ) { _grantRole(DEFAULT_ADMIN_ROLE, admin); implementation = implementation_; policyManager = policyManager_; debtRegistry = debtRegistry_; complianceRegistry = complianceRegistry_; } /** * @notice Deploys a new eMoneyToken instance as a UUPS proxy * @dev Requires TOKEN_DEPLOYER_ROLE. Creates ERC1967Proxy, initializes token, and configures PolicyManager. * @param name Token name (e.g., "USD eMoney") * @param symbol Token symbol (e.g., "USDe") * @param config Token configuration (decimals, issuer, lien mode, bridge settings) * @return token Address of the deployed proxy token contract */ function deployToken( string calldata name, string calldata symbol, TokenConfig calldata config ) external override onlyRole(TOKEN_DEPLOYER_ROLE) returns (address token) { if (config.issuer == address(0)) revert ZeroIssuer(); if (config.defaultLienMode != 1 && config.defaultLienMode != 2) { revert PolicyInvalidLienMode(config.defaultLienMode); } // Deploy UUPS proxy bytes memory initData = abi.encodeWithSelector( IeMoneyToken.initialize.selector, name, symbol, config.decimals, config.issuer, policyManager, debtRegistry, complianceRegistry ); ERC1967Proxy proxy = new ERC1967Proxy(implementation, initData); token = address(proxy); // Configure token in PolicyManager IPolicyManager(policyManager).setLienMode(token, config.defaultLienMode); IPolicyManager(policyManager).setBridgeOnly(token, config.bridgeOnly); if (config.bridge != address(0)) { IPolicyManager(policyManager).setBridge(token, config.bridge); } // Register token by code hash (deterministic based on deployment params) // Include token address, timestamp, and block number to prevent collisions bytes32 codeHash = keccak256(abi.encodePacked(name, symbol, config.issuer, token, block.timestamp, block.number)); _tokensByCodeHash[codeHash] = token; emit TokenDeployed( token, codeHash, name, symbol, config.decimals, config.issuer, config.defaultLienMode, config.bridgeOnly, config.bridge ); } /** * @notice Returns the token address for a given code hash * @dev Code hash is generated deterministically during token deployment * @param codeHash The code hash to lookup * @return Token address (zero address if not found) */ function tokenByCodeHash(bytes32 codeHash) external view override returns (address) { return _tokensByCodeHash[codeHash]; } }