// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; /** * @title BridgeModuleRegistry * @notice Registry for bridge modules (hooks, validators, fee calculators) * @dev Enables extending bridge functionality without modifying core contracts */ contract BridgeModuleRegistry is Initializable, AccessControlUpgradeable, UUPSUpgradeable { bytes32 public constant MODULE_ADMIN_ROLE = keccak256("MODULE_ADMIN_ROLE"); bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); enum ModuleType { PreBridgeHook, // Execute before bridge (e.g., compliance check) PostBridgeHook, // Execute after bridge (e.g., notification) FeeCalculator, // Custom fee calculation RateLimiter, // Rate limiting logic Validator // Custom validation } struct Module { address implementation; bool active; uint256 priority; uint256 registeredAt; } // Storage mapping(ModuleType => address[]) public modules; mapping(ModuleType => mapping(address => Module)) public moduleInfo; mapping(ModuleType => uint256) public moduleCount; event ModuleRegistered( ModuleType indexed moduleType, address indexed module, uint256 priority ); event ModuleUnregistered( ModuleType indexed moduleType, address indexed module ); event ModuleExecuted( ModuleType indexed moduleType, address indexed module, bool success ); /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize(address admin) external initializer { __AccessControl_init(); __UUPSUpgradeable_init(); _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(MODULE_ADMIN_ROLE, admin); _grantRole(UPGRADER_ROLE, admin); } function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {} /** * @notice Register module */ function registerModule( ModuleType moduleType, address module, uint256 priority ) external onlyRole(MODULE_ADMIN_ROLE) { require(module != address(0), "Zero address"); require(module.code.length > 0, "Not a contract"); require(moduleInfo[moduleType][module].implementation == address(0), "Already registered"); modules[moduleType].push(module); moduleInfo[moduleType][module] = Module({ implementation: module, active: true, priority: priority, registeredAt: block.timestamp }); moduleCount[moduleType]++; emit ModuleRegistered(moduleType, module, priority); } /** * @notice Unregister module */ function unregisterModule( ModuleType moduleType, address module ) external onlyRole(MODULE_ADMIN_ROLE) { require(moduleInfo[moduleType][module].implementation != address(0), "Not registered"); moduleInfo[moduleType][module].active = false; moduleCount[moduleType]--; emit ModuleUnregistered(moduleType, module); } /** * @notice Execute all modules of a type */ function executeModules( ModuleType moduleType, bytes calldata data ) external returns (bytes[] memory results) { address[] memory activeModules = modules[moduleType]; uint256 activeCount = 0; // Count active modules for (uint256 i = 0; i < activeModules.length; i++) { if (moduleInfo[moduleType][activeModules[i]].active) { activeCount++; } } results = new bytes[](activeCount); uint256 resultIndex = 0; // Execute each active module for (uint256 i = 0; i < activeModules.length; i++) { address module = activeModules[i]; if (!moduleInfo[moduleType][module].active) continue; (bool success, bytes memory result) = module.call(data); emit ModuleExecuted(moduleType, module, success); if (success) { results[resultIndex] = result; resultIndex++; } } return results; } /** * @notice Execute single module */ function executeModule( ModuleType moduleType, address module, bytes calldata data ) external returns (bytes memory result) { require(moduleInfo[moduleType][module].active, "Module not active"); (bool success, bytes memory returnData) = module.call(data); emit ModuleExecuted(moduleType, module, success); require(success, "Module execution failed"); return returnData; } /** * @notice Set module priority */ function setModulePriority( ModuleType moduleType, address module, uint256 priority ) external onlyRole(MODULE_ADMIN_ROLE) { require(moduleInfo[moduleType][module].implementation != address(0), "Not registered"); moduleInfo[moduleType][module].priority = priority; } // View functions function getModules(ModuleType moduleType) external view returns (address[] memory) { return modules[moduleType]; } function getActiveModules(ModuleType moduleType) external view returns (address[] memory) { address[] memory allModules = modules[moduleType]; uint256 activeCount = 0; for (uint256 i = 0; i < allModules.length; i++) { if (moduleInfo[moduleType][allModules[i]].active) { activeCount++; } } address[] memory activeModules = new address[](activeCount); uint256 index = 0; for (uint256 i = 0; i < allModules.length; i++) { if (moduleInfo[moduleType][allModules[i]].active) { activeModules[index] = allModules[i]; index++; } } return activeModules; } function getModuleInfo(ModuleType moduleType, address module) external view returns (Module memory) { return moduleInfo[moduleType][module]; } function getModuleCount(ModuleType moduleType) external view returns (uint256) { return moduleCount[moduleType]; } function isModuleActive(ModuleType moduleType, address module) external view returns (bool) { return moduleInfo[moduleType][module].active; } }