// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "./ISO4217WToken.sol"; import "./interfaces/ITokenRegistry.sol"; import "./interfaces/IComplianceGuard.sol"; import "./libraries/ISO4217WCompliance.sol"; /** * @title TokenFactory * @notice Factory for deploying ISO-4217 W tokens * @dev Creates UUPS upgradeable proxy tokens with proper configuration */ contract TokenFactory is AccessControl { bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE"); address public immutable tokenImplementation; ITokenRegistry public tokenRegistry; IComplianceGuard public complianceGuard; address public reserveOracle; address public mintController; address public burnController; event TokenDeployed( address indexed token, string indexed currencyCode, string tokenSymbol, address indexed custodian ); constructor( address admin, address tokenImplementation_, address tokenRegistry_, address complianceGuard_, address reserveOracle_, address mintController_, address burnController_ ) { _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(DEPLOYER_ROLE, admin); tokenImplementation = tokenImplementation_; tokenRegistry = ITokenRegistry(tokenRegistry_); complianceGuard = IComplianceGuard(complianceGuard_); reserveOracle = reserveOracle_; mintController = mintController_; burnController = burnController_; } /** * @notice Deploy a new ISO-4217 W token * @param currencyCode ISO-4217 currency code (e.g., "USD") * @param name Token name (e.g., "USDW Token") * @param symbol Token symbol (must be W, e.g., "USDW") * @param decimals Token decimals (typically 2 for fiat) * @param custodian Custodian address * @return token Address of deployed token */ function deployToken( string memory currencyCode, string memory name, string memory symbol, uint8 decimals, address custodian ) external onlyRole(DEPLOYER_ROLE) returns (address token) { // Validate ISO-4217 format require( ISO4217WCompliance.isValidISO4217Format(currencyCode), "TokenFactory: invalid ISO-4217 format" ); // Validate GRU isolation require( !ISO4217WCompliance.violatesGRUIsolation(currencyCode), "TokenFactory: GRU isolation violation" ); // Validate token symbol matches W pattern require( ISO4217WCompliance.validateTokenSymbol(currencyCode, symbol), "TokenFactory: token symbol must be W" ); require(custodian != address(0), "TokenFactory: zero custodian"); require(bytes(name).length > 0, "TokenFactory: empty name"); require(bytes(symbol).length > 0, "TokenFactory: empty symbol"); // Deploy UUPS proxy bytes memory initData = abi.encodeWithSelector( ISO4217WToken.initialize.selector, name, symbol, currencyCode, decimals, custodian, mintController, burnController, address(complianceGuard), msg.sender // Admin ); ERC1967Proxy proxy = new ERC1967Proxy(tokenImplementation, initData); token = address(proxy); // Grant reserve update role to oracle ISO4217WToken(token).grantRole(keccak256("RESERVE_UPDATE_ROLE"), reserveOracle); // Register token in registry tokenRegistry.registerToken(currencyCode, token, symbol, decimals, custodian); tokenRegistry.setMintController(currencyCode, mintController); tokenRegistry.setBurnController(currencyCode, burnController); emit TokenDeployed(token, currencyCode, symbol, custodian); } /** * @notice Set system contract addresses */ function setTokenRegistry(address tokenRegistry_) external onlyRole(DEFAULT_ADMIN_ROLE) { require(tokenRegistry_ != address(0), "TokenFactory: zero address"); tokenRegistry = ITokenRegistry(tokenRegistry_); } function setComplianceGuard(address complianceGuard_) external onlyRole(DEFAULT_ADMIN_ROLE) { require(complianceGuard_ != address(0), "TokenFactory: zero address"); complianceGuard = IComplianceGuard(complianceGuard_); } function setReserveOracle(address reserveOracle_) external onlyRole(DEFAULT_ADMIN_ROLE) { require(reserveOracle_ != address(0), "TokenFactory: zero address"); reserveOracle = reserveOracle_; } function setMintController(address mintController_) external onlyRole(DEFAULT_ADMIN_ROLE) { require(mintController_ != address(0), "TokenFactory: zero address"); mintController = mintController_; } function setBurnController(address burnController_) external onlyRole(DEFAULT_ADMIN_ROLE) { require(burnController_ != address(0), "TokenFactory: zero address"); burnController = burnController_; } }