diff --git a/src/contracts/core/ProtocolRegistry.sol b/src/contracts/core/ProtocolRegistry.sol new file mode 100644 index 0000000000..a66f360525 --- /dev/null +++ b/src/contracts/core/ProtocolRegistry.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import "./ProtocolRegistryStorage.sol"; + +contract ProtocolRegistry is Initializable, OwnableUpgradeable, ProtocolRegistryStorage { + using ShortStringsUpgradeable for *; + + /** + * + * INITIALIZING FUNCTIONS + * + */ + constructor() { + _disableInitializers(); + } + + /// @inheritdoc IProtocolRegistry + function initialize( + address initialOwner + ) external initializer { + _transferOwnership(initialOwner); + } + + /** + * + * INITIALIZING FUNCTIONS + * + */ + + /// @inheritdoc IProtocolRegistry + function setVersion(address addr, string calldata semver) external onlyOwner { + _setVersion(addr, semver); + } + + /// @inheritdoc IProtocolRegistry + function setVersions(address[] calldata addresses, string calldata semver) external onlyOwner { + for (uint256 i = 0; i < addresses.length; ++i) { + _setVersion(addresses[i], semver); + } + } + + /// @inheritdoc IProtocolRegistry + function setVersions(address[] calldata addresses, string[] calldata semvers) external onlyOwner { + require(addresses.length == semvers.length, InputArrayLengthMismatch()); + for (uint256 i = 0; i < addresses.length; ++i) { + _setVersion(addresses[i], semvers[i]); + } + } + + /// @dev Internal function to set the version for a given address. + function _setVersion(address addr, string calldata semver) internal { + _semver[addr] = semver.toShortString(); + emit VersionSet(addr, semver); + } + + /** + * + * VIEW FUNCTIONS + * + */ + + /// @inheritdoc IProtocolRegistry + function version( + address addr + ) external view returns (string memory) { + return _semver[addr].toString(); + } + + /// @inheritdoc IProtocolRegistry + function majorVersion( + address addr + ) external view returns (string memory) { + bytes memory v = bytes(_semver[addr].toString()); + return string(abi.encodePacked(v[0])); + } +} diff --git a/src/contracts/core/ProtocolRegistryStorage.sol b/src/contracts/core/ProtocolRegistryStorage.sol new file mode 100644 index 0000000000..32e6260a59 --- /dev/null +++ b/src/contracts/core/ProtocolRegistryStorage.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin-upgrades/contracts/utils/ShortStringsUpgradeable.sol"; +import "../interfaces/IProtocolRegistry.sol"; + +abstract contract ProtocolRegistryStorage is IProtocolRegistry { + /// @notice Mapping from an address to its semantic version. + mapping(address addr => ShortString semver) internal _semver; + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[49] private __gap; +} diff --git a/src/contracts/interfaces/IProtocolRegistry.sol b/src/contracts/interfaces/IProtocolRegistry.sol new file mode 100644 index 0000000000..9677a070f8 --- /dev/null +++ b/src/contracts/interfaces/IProtocolRegistry.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +interface IProtocolRegistryErrors { + /// @notice Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); +} + +interface IProtocolRegistryTypes {} + +interface IProtocolRegistryEvents is IProtocolRegistryTypes { + /// @notice Emitted when the version is set for a given address. + /// @param addr The address for which the version is set. + /// @param semver The semantic version string set for the address. + event VersionSet(address indexed addr, string semver); +} + +interface IProtocolRegistry is IProtocolRegistryErrors, IProtocolRegistryEvents { + /** + * @notice Initializes the ProtocolRegistry with the initial owner. + * @param initialOwner The address to set as the initial owner. + */ + function initialize( + address initialOwner + ) external; + + /** + * @notice Sets the semantic version for a single address. + * @dev Only callable by the contract owner. + * @param addr The address for which to set the version. + * @param semver The semantic version string to set for the address. + */ + function setVersion(address addr, string calldata semver) external; + + /** + * @notice Sets the same semantic version for each address in the provided array. + * @dev Only callable by the contract owner. + * @param addresses The addresses for which to set the version. + * @param semver The semantic version string to set for all addresses. + */ + function setVersions(address[] calldata addresses, string calldata semver) external; + + /** + * @notice Sets a distinct semantic version for each address in the provided array. + * @dev Only callable by the contract owner. + * @param addresses The addresses for which to set the version. + * @param semvers The semantic version strings to set, one for each address. + */ + function setVersions(address[] calldata addresses, string[] calldata semvers) external; + + /** + * @notice Returns the semantic version string for a given address. + * @param addr The address to query. + * @return The semantic version string associated with the address. + */ + function version( + address addr + ) external view returns (string memory); + + /** + * @notice Returns the major version string for a given address. + * @param addr The address to query. + * @return The major version string associated with the address. + */ + function majorVersion( + address addr + ) external view returns (string memory); +} diff --git a/src/test/harnesses/EigenPodHarness.sol b/src/test/harnesses/EigenPodHarness.sol index 26e3fb5816..8d590c4447 100644 --- a/src/test/harnesses/EigenPodHarness.sol +++ b/src/test/harnesses/EigenPodHarness.sol @@ -5,9 +5,7 @@ import "../../contracts/pods/EigenPod.sol"; import "forge-std/Test.sol"; contract EigenPodHarness is EigenPod { - constructor(IETHPOSDeposit _ethPOS, IEigenPodManager _eigenPodManager) - EigenPod(_ethPOS, _eigenPodManager) - {} + constructor(IETHPOSDeposit _ethPOS, IEigenPodManager _eigenPodManager) EigenPod(_ethPOS, _eigenPodManager) {} function getActiveValidatorCount() public view returns (uint) { return activeValidatorCount; diff --git a/src/test/harnesses/EigenPodManagerWrapper.sol b/src/test/harnesses/EigenPodManagerWrapper.sol index b53aba3366..bc4a8b6d31 100644 --- a/src/test/harnesses/EigenPodManagerWrapper.sol +++ b/src/test/harnesses/EigenPodManagerWrapper.sol @@ -5,12 +5,9 @@ import "../../contracts/pods/EigenPodManager.sol"; ///@notice This contract exposes a manual setter for podShares in order to initialize podShares as negative contract EigenPodManagerWrapper is EigenPodManager { - constructor( - IETHPOSDeposit _ethPOS, - IBeacon _eigenPodBeacon, - IDelegationManager _delegationManager, - IPauserRegistry _pauserRegistry - ) EigenPodManager(_ethPOS, _eigenPodBeacon, _delegationManager, _pauserRegistry) {} + constructor(IETHPOSDeposit _ethPOS, IBeacon _eigenPodBeacon, IDelegationManager _delegationManager, IPauserRegistry _pauserRegistry) + EigenPodManager(_ethPOS, _eigenPodBeacon, _delegationManager, _pauserRegistry) + {} function setPodOwnerShares(address owner, IEigenPod pod) external { ownerToPod[owner] = pod; diff --git a/src/test/integration/IntegrationDeployer.t.sol b/src/test/integration/IntegrationDeployer.t.sol index 6a81f2f76a..907a14a2a3 100644 --- a/src/test/integration/IntegrationDeployer.t.sol +++ b/src/test/integration/IntegrationDeployer.t.sol @@ -330,12 +330,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { /// Deploy an implementation contract for each contract in the system function _deployImplementations() public { allocationManagerImplementation = new AllocationManager( - delegationManager, - eigenStrategy, - eigenLayerPauserReg, - permissionController, - DEALLOCATION_DELAY, - ALLOCATION_CONFIGURATION_DELAY + delegationManager, eigenStrategy, eigenLayerPauserReg, permissionController, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY ); permissionControllerImplementation = new PermissionController(); delegationManagerImplementation = new DelegationManager( @@ -363,8 +358,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { }) ); avsDirectoryImplementation = new AVSDirectory(delegationManager, eigenLayerPauserReg, version); - eigenPodManagerImplementation = - new EigenPodManager(DEPOSIT_CONTRACT, eigenPodBeacon, delegationManager, eigenLayerPauserReg); + eigenPodManagerImplementation = new EigenPodManager(DEPOSIT_CONTRACT, eigenPodBeacon, delegationManager, eigenLayerPauserReg); strategyFactoryImplementation = new StrategyFactory(strategyManager, eigenLayerPauserReg); // Beacon implementations diff --git a/src/test/unit/BN254CertificateVerifierUnit.t.sol b/src/test/unit/BN254CertificateVerifierUnit.t.sol index a12f60f976..5fab075f21 100644 --- a/src/test/unit/BN254CertificateVerifierUnit.t.sol +++ b/src/test/unit/BN254CertificateVerifierUnit.t.sol @@ -60,8 +60,7 @@ contract BN254CertificateVerifierUnitTests is defaultOperatorSetConfig = OperatorSetConfig({owner: operatorSetOwner, maxStalenessPeriod: defaultMaxStaleness}); // Deploy Contracts - bn254CertificateVerifierImplementation = - new BN254CertificateVerifier(IOperatorTableUpdater(address(operatorTableUpdaterMock))); + bn254CertificateVerifierImplementation = new BN254CertificateVerifier(IOperatorTableUpdater(address(operatorTableUpdaterMock))); verifier = BN254CertificateVerifier( address(new TransparentUpgradeableProxy(address(bn254CertificateVerifierImplementation), address(eigenLayerProxyAdmin), "")) ); diff --git a/src/test/unit/EigenPodManagerUnit.t.sol b/src/test/unit/EigenPodManagerUnit.t.sol index 97a08ced75..950a4cde5e 100644 --- a/src/test/unit/EigenPodManagerUnit.t.sol +++ b/src/test/unit/EigenPodManagerUnit.t.sol @@ -233,9 +233,8 @@ contract EigenPodManagerUnitTests_ShareUpdateTests is EigenPodManagerUnitTests { super.setUp(); // Upgrade eigenPodManager to wrapper - eigenPodManagerWrapper = new EigenPodManagerWrapper( - ethPOSMock, eigenPodBeacon, IDelegationManager(address(delegationManagerMock)), pauserRegistry - ); + eigenPodManagerWrapper = + new EigenPodManagerWrapper(ethPOSMock, eigenPodBeacon, IDelegationManager(address(delegationManagerMock)), pauserRegistry); eigenLayerProxyAdmin.upgrade(ITransparentUpgradeableProxy(payable(address(eigenPodManager))), address(eigenPodManagerWrapper)); } @@ -424,9 +423,8 @@ contract EigenPodManagerUnitTests_WithdrawSharesAsTokensTests is EigenPodManager super.setUp(); // Upgrade eigenPodManager to wrapper - eigenPodManagerWrapper = new EigenPodManagerWrapper( - ethPOSMock, eigenPodBeacon, IDelegationManager(address(delegationManagerMock)), pauserRegistry - ); + eigenPodManagerWrapper = + new EigenPodManagerWrapper(ethPOSMock, eigenPodBeacon, IDelegationManager(address(delegationManagerMock)), pauserRegistry); eigenLayerProxyAdmin.upgrade(ITransparentUpgradeableProxy(payable(address(eigenPodManager))), address(eigenPodManagerWrapper)); } /** @@ -545,9 +543,8 @@ contract EigenPodManagerUnitTests_BeaconChainETHBalanceUpdateTests is EigenPodMa super.setUp(); // Upgrade eigenPodManager to wrapper - eigenPodManagerWrapper = new EigenPodManagerWrapper( - ethPOSMock, eigenPodBeacon, IDelegationManager(address(delegationManagerMock)), pauserRegistry - ); + eigenPodManagerWrapper = + new EigenPodManagerWrapper(ethPOSMock, eigenPodBeacon, IDelegationManager(address(delegationManagerMock)), pauserRegistry); eigenLayerProxyAdmin.upgrade(ITransparentUpgradeableProxy(payable(address(eigenPodManager))), address(eigenPodManagerWrapper)); } diff --git a/src/test/unit/TaskMailboxUnit.t.sol b/src/test/unit/TaskMailboxUnit.t.sol index 5f67e8c8e2..7c08c8d486 100644 --- a/src/test/unit/TaskMailboxUnit.t.sol +++ b/src/test/unit/TaskMailboxUnit.t.sol @@ -1249,8 +1249,7 @@ contract TaskMailboxUnitTests_submitResult is TaskMailboxUnitTests { // Deploy a new TaskMailbox with the failing verifier using proxy pattern ProxyAdmin proxyAdmin = new ProxyAdmin(); - TaskMailbox taskMailboxImpl = - new TaskMailbox(address(mockFailingVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); + TaskMailbox taskMailboxImpl = new TaskMailbox(address(mockFailingVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( address(taskMailboxImpl), address(proxyAdmin), @@ -3270,8 +3269,7 @@ contract TaskMailboxUnitTests_Upgradeable is TaskMailboxUnitTests { function test_Implementation_CannotBeInitialized() public { // Deploy a new implementation - TaskMailbox newImpl = - new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); + TaskMailbox newImpl = new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); // Try to initialize the implementation directly, should revert vm.expectRevert("Initializable: contract is already initialized"); @@ -3282,8 +3280,7 @@ contract TaskMailboxUnitTests_Upgradeable is TaskMailboxUnitTests { address newOwner = address(0x1234); // Deploy new implementation with different version - TaskMailbox newImpl = - new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); + TaskMailbox newImpl = new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); // Upgrade proxy to new implementation proxyAdmin.upgrade(ITransparentUpgradeableProxy(address(taskMailbox)), address(newImpl)); @@ -3296,8 +3293,7 @@ contract TaskMailboxUnitTests_Upgradeable is TaskMailboxUnitTests { address attacker = address(0x9999); // Deploy new implementation - TaskMailbox newImpl = - new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); + TaskMailbox newImpl = new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); // Try to upgrade from non-owner, should revert vm.prank(attacker); @@ -3331,8 +3327,7 @@ contract TaskMailboxUnitTests_Upgradeable is TaskMailboxUnitTests { assertEq(address(retrievedConfig.taskHook), address(config.taskHook)); // Deploy new implementation - TaskMailbox newImpl = - new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); + TaskMailbox newImpl = new TaskMailbox(address(mockBN254CertificateVerifier), address(mockECDSACertificateVerifier), MAX_TASK_SLA); // Upgrade vm.prank(address(this)); // proxyAdmin owner