diff --git a/contracts/src/core/MachServiceManagerRegistry.sol b/contracts/src/core/MachServiceManagerRegistry.sol index a8265b6..a44b819 100644 --- a/contracts/src/core/MachServiceManagerRegistry.sol +++ b/contracts/src/core/MachServiceManagerRegistry.sol @@ -9,26 +9,26 @@ pragma solidity =0.8.12; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import {IMachServiceManager} from "../interfaces/IMachServiceManager.sol"; +import {ITotalAlerts, ITotalAlertsLegacy} from "../interfaces/IMachServiceManager.sol"; import {ZeroAddress, AlreadyAdded, NotAdded} from "../error/Errors.sol"; /// @title MachServiceManagerRegistry /// @notice This contract allows the owner to register service managers for specific rollup chain IDs and check if they have active alerts. contract MachServiceManagerRegistry is OwnableUpgradeable { // Mapping of rollup chain ID to service manager - mapping(uint256 => IMachServiceManager) public serviceManagers; + mapping(uint256 => address) public serviceManagers; /// @notice Emitted when a service manager is registered /// @param rollupChainId The rollup chain ID /// @param serviceManager The registered service manager /// @param sender The address that registered the service manager - event ServiceManagerRegistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender); + event ServiceManagerRegistered(uint256 indexed rollupChainId, address serviceManager, address sender); /// @notice Emitted when a service manager is deregistered /// @param rollupChainId The rollup chain ID /// @param serviceManager The deregistered service manager /// @param sender The address that deregistered the service manager - event ServiceManagerDeregistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender); + event ServiceManagerDeregistered(uint256 indexed rollupChainId, address serviceManager, address sender); /// @notice Initializes the contract and sets the deployer as the owner function initialize() external initializer { @@ -39,7 +39,7 @@ contract MachServiceManagerRegistry is OwnableUpgradeable { /// @param rollupChainId_ The rollup chain ID /// @param serviceManager_ The service manager to be registered /// @dev Reverts if the service manager address is zero or already registered - function registerServiceManager(uint256 rollupChainId_, IMachServiceManager serviceManager_) external onlyOwner { + function registerServiceManager(uint256 rollupChainId_, address serviceManager_) external onlyOwner { if (address(serviceManager_) == address(0)) { revert ZeroAddress(); } @@ -54,7 +54,7 @@ contract MachServiceManagerRegistry is OwnableUpgradeable { /// @param rollupChainId_ The rollup chain ID /// @param serviceManager_ The service manager to be deregistered /// @dev Reverts if the service manager is not already registered - function deregisterServiceManager(uint256 rollupChainId_, IMachServiceManager serviceManager_) external onlyOwner { + function deregisterServiceManager(uint256 rollupChainId_, address serviceManager_) external onlyOwner { if (serviceManagers[rollupChainId_] != serviceManager_) { revert NotAdded(); } @@ -66,6 +66,15 @@ contract MachServiceManagerRegistry is OwnableUpgradeable { /// @param rollupChainId_ The rollup chain ID /// @return True if there are active alerts, false otherwise function hasActiveAlerts(uint256 rollupChainId_) external view returns (bool) { - return serviceManagers[rollupChainId_].totalAlerts(rollupChainId_) > 0; + address target = serviceManagers[rollupChainId_]; + if (target == address(0)) { + return false; + } + + try ITotalAlerts(target).totalAlerts(rollupChainId_) returns (uint256 totalAlerts) { + return totalAlerts > 0; + } catch (bytes memory reason) { + return ITotalAlertsLegacy(target).totalAlerts() > 0; + } } } diff --git a/contracts/src/interfaces/IMachServiceManager.sol b/contracts/src/interfaces/IMachServiceManager.sol index d93872b..ea4bb63 100644 --- a/contracts/src/interfaces/IMachServiceManager.sol +++ b/contracts/src/interfaces/IMachServiceManager.sol @@ -5,6 +5,17 @@ import {IServiceManager} from "eigenlayer-middleware/interfaces/IServiceManager. import {BLSSignatureChecker} from "eigenlayer-middleware/BLSSignatureChecker.sol"; import {IMachOptimism} from "../interfaces/IMachOptimism.sol"; +interface ITotalAlertsLegacy { + // Legacy + /// @notice Returns the length of total alerts + function totalAlerts() external view returns (uint256); +} + +interface ITotalAlerts { + /// @notice Returns the length of total alerts + function totalAlerts(uint256 rollupChainId) external view returns (uint256); +} + /** * @title Interface for the MachServiceManager contract. * @author Altlayer, Inc. diff --git a/contracts/test/MachServiceManagerRegistry.t.sol b/contracts/test/MachServiceManagerRegistry.t.sol index 217061a..2e545d5 100644 --- a/contracts/test/MachServiceManagerRegistry.t.sol +++ b/contracts/test/MachServiceManagerRegistry.t.sol @@ -7,20 +7,46 @@ import "../src/interfaces/IMachServiceManager.sol"; import "../src/core/MachServiceManagerRegistry.sol"; import "../src/error/Errors.sol"; +contract MockServiceManager is ITotalAlerts { + uint256 public alerts; + + function setAlerts(uint256 _alerts) public { + alerts = _alerts; + } + + function totalAlerts(uint256) external view override returns (uint256) { + return alerts; + } +} + +contract MockServiceManagerLegacy is ITotalAlertsLegacy { + uint256 public alerts; + + function setAlerts(uint256 _alerts) public { + alerts = _alerts; + } + + function totalAlerts() external view override returns (uint256) { + return alerts; + } +} + contract MachServiceManagerRegistryTest is Test { MachServiceManagerRegistry registry; - IMachServiceManager mockServiceManager; + MockServiceManager mockServiceManager; + MockServiceManagerLegacy mockServiceManagerLegacy; - event ServiceManagerRegistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender); - event ServiceManagerDeregistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender); + event ServiceManagerRegistered(uint256 indexed rollupChainId, address serviceManager, address sender); + event ServiceManagerDeregistered(uint256 indexed rollupChainId, address serviceManager, address sender); function setUp() public { // Deploy the registry registry = new MachServiceManagerRegistry(); registry.initialize(); - // Mocking the IMachServiceManager interface - mockServiceManager = IMachServiceManager(address(101)); + // Deploy the mock service managers + mockServiceManager = new MockServiceManager(); + mockServiceManagerLegacy = new MockServiceManagerLegacy(); // Ensure registry is owned by this test contract for testing purposes registry.transferOwnership(address(this)); @@ -30,9 +56,9 @@ contract MachServiceManagerRegistryTest is Test { uint256 rollupChainId = 1; vm.expectEmit(); - emit ServiceManagerRegistered(rollupChainId, mockServiceManager, address(this)); + emit ServiceManagerRegistered(rollupChainId, address(mockServiceManager), address(this)); - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); assertEq( address(registry.serviceManagers(rollupChainId)), address(mockServiceManager), "Service manager mismatch" @@ -43,16 +69,16 @@ contract MachServiceManagerRegistryTest is Test { uint256 rollupChainId = 1; vm.expectRevert(ZeroAddress.selector); - registry.registerServiceManager(rollupChainId, IMachServiceManager(address(0))); + registry.registerServiceManager(rollupChainId, address(0)); } function test_RegisterServiceManager_RevertIfAlreadyAdded() public { uint256 rollupChainId = 1; - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); vm.expectRevert(AlreadyAdded.selector); - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); } function test_RegisterServiceManager_RevertIfNotOwner() public { @@ -62,52 +88,75 @@ contract MachServiceManagerRegistryTest is Test { registry.transferOwnership(address(0xdead)); vm.expectRevert("Ownable: caller is not the owner"); - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); } function test_HasActiveAlerts() public { uint256 rollupChainId = 1; - // Mocking the behavior of totalAlerts to return a non-zero value - vm.mockCall( - address(mockServiceManager), - abi.encodeWithSelector(IMachServiceManager.totalAlerts.selector, rollupChainId), - abi.encode(1) - ); + // Set alerts to a non-zero value + mockServiceManager.setAlerts(1); - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); bool hasAlerts = registry.hasActiveAlerts(rollupChainId); assertTrue(hasAlerts, "Expected to have active alerts"); } + function test_HasActiveAlerts_NoServiceManager() public { + bool hasAlerts = registry.hasActiveAlerts(99); // no service manager registerd for the rollup ID 99 + assertFalse(hasAlerts, "Expected to have no active alerts"); + } + function test_HasActiveAlerts_NoAlerts() public { uint256 rollupChainId = 1; - // Mocking the behavior of totalAlerts to return zero - vm.mockCall( - address(mockServiceManager), - abi.encodeWithSelector(IMachServiceManager.totalAlerts.selector, rollupChainId), - abi.encode(0) - ); + // Set alerts to zero + mockServiceManager.setAlerts(0); - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); bool hasAlerts = registry.hasActiveAlerts(rollupChainId); assertFalse(hasAlerts, "Expected to have no active alerts"); } + function test_HasActiveAlerts_LegacyAlerts() public { + uint256 rollupChainId = 1; + + // Set alerts to a non-zero value (simulating legacy alerts) + mockServiceManagerLegacy.setAlerts(1); + + registry.registerServiceManager(rollupChainId, address(mockServiceManagerLegacy)); + + bool hasAlerts = registry.hasActiveAlerts(rollupChainId); + + assertTrue(hasAlerts, "Expected to have active alerts in legacy mode"); + } + + function test_HasActiveAlerts_LegacyNoAlerts() public { + uint256 rollupChainId = 1; + + // Set alerts to zero (simulating no legacy alerts) + mockServiceManagerLegacy.setAlerts(0); + + registry.registerServiceManager(rollupChainId, address(mockServiceManagerLegacy)); + + bool hasAlerts = registry.hasActiveAlerts(rollupChainId); + + assertFalse(hasAlerts, "Expected to have no active alerts in legacy mode"); + } + function test_DeregisterServiceManager() public { uint256 rollupChainId = 1; - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); vm.expectEmit(); - emit ServiceManagerDeregistered(rollupChainId, mockServiceManager, address(this)); + emit ServiceManagerDeregistered(rollupChainId, address(mockServiceManager), address(this)); - registry.deregisterServiceManager(rollupChainId, mockServiceManager); + registry.deregisterServiceManager(rollupChainId, address(mockServiceManager)); assertEq(address(registry.serviceManagers(rollupChainId)), address(0), "Service manager should be deregistered"); } @@ -116,19 +165,19 @@ contract MachServiceManagerRegistryTest is Test { uint256 rollupChainId = 1; vm.expectRevert(NotAdded.selector); - registry.deregisterServiceManager(rollupChainId, mockServiceManager); + registry.deregisterServiceManager(rollupChainId, address(mockServiceManager)); } function test_DeregisterServiceManager_RevertIfNotOwner() public { uint256 rollupChainId = 1; // Register a service manager - registry.registerServiceManager(rollupChainId, mockServiceManager); + registry.registerServiceManager(rollupChainId, address(mockServiceManager)); // Transfer ownership to another address for this test registry.transferOwnership(address(0xdead)); vm.expectRevert("Ownable: caller is not the owner"); - registry.deregisterServiceManager(rollupChainId, mockServiceManager); + registry.deregisterServiceManager(rollupChainId, address(mockServiceManager)); } }