// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "./IRWATokenFactory.sol"; import "./RWAToken.sol"; import "./IRWATokenRegistry.sol"; import "./libraries/RWATaxonomy.sol"; import "./IUniversalAssetRegistryRWA.sol"; /** * @title RWATokenFactory * @notice Canonical factory for Li* M00 commodity index tokens (RWA indices, not c* eMoney). * @dev One active deployment per indexTicker per chain. Taxonomy aligned with * config/rwa-capital-markets-taxonomy.v1.json (off-chain source of product metadata). */ contract RWATokenFactory is AccessControl, ReentrancyGuard, IRWATokenFactory { bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE"); IRWATokenRegistry private immutable _rwaTokenRegistry; address private immutable _universalAssetRegistry; constructor( address admin, address rwaTokenRegistry_, address universalAssetRegistry_ ) { require(rwaTokenRegistry_ != address(0), "RWATokenFactory: registry"); _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(DEPLOYER_ROLE, admin); _rwaTokenRegistry = IRWATokenRegistry(rwaTokenRegistry_); _universalAssetRegistry = universalAssetRegistry_; } function rwaTokenRegistry() external view returns (address) { return address(_rwaTokenRegistry); } function universalAssetRegistry() external view returns (address) { return _universalAssetRegistry; } function deployRWAIndex(RWAProductConfig calldata config) external onlyRole(DEPLOYER_ROLE) nonReentrant returns (address token) { RWATaxonomy.validateProduct(config.indexTicker, config.assetClass, config.gruLayer); require( !_rwaTokenRegistry.isRegistered(config.indexTicker), "RWATokenFactory: already registered" ); require(config.initialOwner != address(0), "RWATokenFactory: owner"); require(config.complianceAdmin != address(0), "RWATokenFactory: compliance"); require(config.indexPublisher != address(0), "RWATokenFactory: publisher"); require(config.methodologyDocumentHash != bytes32(0), "RWATokenFactory: methodology"); RWAToken deployed = new RWAToken( config.name, config.symbol, config.decimals, config.indexTicker, config.assetClass, config.assetGroup, config.instrumentType, config.underlyingAsset, config.gruLayer, config.initialOwner, config.complianceAdmin, config.indexPublisher, config.initialIndexValue, config.initialSupply, config.methodologyDocumentHash ); token = address(deployed); _rwaTokenRegistry.registerIndex(config.indexTicker, token, config.symbol); if (config.registerInUniversalAssetRegistry && _universalAssetRegistry != address(0)) { IUniversalAssetRegistryRWA(_universalAssetRegistry).registerRWAIndexAsset( token, config.name, config.symbol, config.decimals, config.jurisdiction ); } emit RWAIndexDeployed(config.indexTicker, token, config.symbol, config.initialOwner); } }