feat: add version management to ServiceManager contract

Add on-chain version tracking with _version state variable, initialVersion
parameter, versionUpdater role, DATAHAVEN_VERSION() getter, and
updateVersion() function. Emit VersionUpdated and VersionUpdaterSet events.
This commit is contained in:
Gonza Montiel 2026-02-09 16:24:34 -03:00
parent b99a7f90ee
commit eef05bfb0d
4 changed files with 133 additions and 10 deletions

View file

@ -29,6 +29,7 @@ import {IDataHavenServiceManager} from "./interfaces/IDataHavenServiceManager.so
/**
* @title DataHaven ServiceManager contract
* @notice Manages validators in the DataHaven network and submits rewards to EigenLayer
* @dev This contract is upgradeable and integrates with EigenLayer's AllocationManager
*/
contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHavenServiceManager {
using SafeERC20 for IERC20;
@ -64,9 +65,19 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
/// @inheritdoc IDataHavenServiceManager
mapping(address => address) public validatorEthAddressToSolochainAddress;
/// @notice Semantic version of the deployed DataHaven AVS stack.
/// Set during initialization based on deployment chain.
/// This should match the `version` field in the corresponding
/// `contracts/deployments/<chain>.json`.
string private _version;
/// @notice The address authorized to update the contract version
/// Typically set to the deployer account to allow version updates during upgrades
address public versionUpdater;
/// @notice Storage gap for upgradeability (must be at end of state variables)
// solhint-disable-next-line var-name-mixedcase
uint256[46] private __GAP;
uint256[44] private __GAP;
// ============ Modifiers ============
@ -88,6 +99,12 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
_;
}
/// @notice Restricts function to the version updater or owner
modifier onlyVersionUpdater() {
_checkVersionUpdater();
_;
}
function _checkRewardsInitiator() internal view {
require(msg.sender == rewardsInitiator, OnlyRewardsInitiator());
}
@ -104,6 +121,12 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
require(msg.sender == address(_ALLOCATION_MANAGER), OnlyAllocationManager());
}
function _checkVersionUpdater() internal view {
require(
msg.sender == versionUpdater || msg.sender == owner(), "Only version updater or owner"
);
}
// ============ Constructor ============
/// @notice Sets the immutable EigenLayer contract references
@ -123,17 +146,26 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
address initialOwner,
address _rewardsInitiator,
IStrategy[] memory validatorsStrategies,
address _snowbridgeGatewayAddress
address _snowbridgeGatewayAddress,
string memory initialVersion,
address _versionUpdater
) public virtual initializer {
require(initialOwner != address(0), ZeroAddress());
require(_rewardsInitiator != address(0), ZeroAddress());
require(_snowbridgeGatewayAddress != address(0), ZeroAddress());
require(_versionUpdater != address(0), ZeroAddress());
require(bytes(initialVersion).length > 0, "Version cannot be empty");
__Ownable_init();
_transferOwnership(initialOwner);
rewardsInitiator = _rewardsInitiator;
emit RewardsInitiatorSet(address(0), _rewardsInitiator);
// Set version from parameter (allows flexibility per deployment environment)
_version = initialVersion;
versionUpdater = _versionUpdater;
emit VersionUpdaterSet(address(0), _versionUpdater);
// Register the DataHaven service in the AllocationManager.
_ALLOCATION_MANAGER.updateAVSMetadataURI(address(this), DATAHAVEN_AVS_METADATA);
@ -149,6 +181,16 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
_snowbridgeGateway = IGatewayV2(_snowbridgeGatewayAddress);
}
// ============ View Functions ============
/// @notice Returns the semantic version of the deployed DataHaven AVS stack
/// @return The version string (e.g., "1.0.0")
function DATAHAVEN_VERSION() external view returns (string memory) {
return _version;
}
// ============ External Functions ============
/// @inheritdoc IDataHavenServiceManager
function sendNewValidatorSet(
uint128 executionFee,
@ -291,6 +333,32 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
);
}
// ============ Version Management ============
/// @notice Updates the contract version (typically called after upgrades)
/// @param newVersion The new semantic version string (e.g., "1.0.1")
/// @dev Only callable by version updater or owner. Used to keep on-chain version in sync after upgrades.
function updateVersion(
string memory newVersion
) external onlyVersionUpdater {
require(bytes(newVersion).length > 0, "Version cannot be empty");
string memory oldVersion = _version;
_version = newVersion;
emit VersionUpdated(oldVersion, newVersion);
}
/// @notice Sets the address authorized to update the contract version
/// @param newVersionUpdater The new version updater address
/// @dev Only callable by owner. Typically set to the deployer account.
function setVersionUpdater(
address newVersionUpdater
) external onlyOwner {
require(newVersionUpdater != address(0), ZeroAddress());
address oldVersionUpdater = versionUpdater;
versionUpdater = newVersionUpdater;
emit VersionUpdaterSet(oldVersionUpdater, newVersionUpdater);
}
// ============ Rewards Functions ============
/// @inheritdoc IDataHavenServiceManager

View file

@ -76,6 +76,16 @@ interface IDataHavenServiceManagerEvents {
/// @param solochainAddress The new solochain address
event SolochainAddressUpdated(address indexed validator, address indexed solochainAddress);
/// @notice Emitted when the contract version is updated
/// @param oldVersion The previous version string
/// @param newVersion The new version string
event VersionUpdated(string oldVersion, string newVersion);
/// @notice Emitted when the version updater address is changed
/// @param oldVersionUpdater The previous version updater address
/// @param newVersionUpdater The new version updater address
event VersionUpdaterSet(address indexed oldVersionUpdater, address indexed newVersionUpdater);
/// @notice Emitted when a batch of slashing request is being successfully slashed
event SlashingComplete();
}
@ -122,12 +132,17 @@ interface IDataHavenServiceManager is
* @param initialOwner Address of the initial owner
* @param rewardsInitiator Address authorized to initiate rewards
* @param validatorsStrategies Array of strategies supported by validators
* @param _snowbridgeGatewayAddress Address of the Snowbridge Gateway
* @param initialVersion Initial semantic version string (e.g., "1.0.0")
* @param _versionUpdater Address authorized to update the contract version
*/
function initialize(
address initialOwner,
address rewardsInitiator,
IStrategy[] memory validatorsStrategies,
address _snowbridgeGatewayAddress
address _snowbridgeGatewayAddress,
string memory initialVersion,
address _versionUpdater
) external;
/**
@ -206,6 +221,24 @@ interface IDataHavenServiceManager is
IStrategy[] calldata _strategies
) external;
/**
* @notice Updates the contract version
* @param newVersion The new semantic version string (e.g., "1.0.1")
* @dev Only callable by version updater or owner. Typically called after contract upgrades.
*/
function updateVersion(
string memory newVersion
) external;
/**
* @notice Sets the address authorized to update the contract version
* @param newVersionUpdater The new version updater address
* @dev Only callable by owner. Typically set to the deployer account.
*/
function setVersionUpdater(
address newVersionUpdater
) external;
// ============ Rewards Submitter Functions ============
/**

View file

@ -73,12 +73,28 @@
"type": "t_mapping(t_address,t_address)"
},
{
"astId": 23790,
"astId": 23788,
"contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager",
"label": "_version",
"offset": 0,
"slot": "105",
"type": "t_string_storage"
},
{
"astId": 23791,
"contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager",
"label": "versionUpdater",
"offset": 0,
"slot": "106",
"type": "t_address"
},
{
"astId": 23796,
"contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager",
"label": "__GAP",
"offset": 0,
"slot": "105",
"type": "t_array(t_uint256)46_storage"
"slot": "107",
"type": "t_array(t_uint256)44_storage"
}
],
"types": {
@ -87,10 +103,10 @@
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)46_storage": {
"t_array(t_uint256)44_storage": {
"encoding": "inplace",
"label": "uint256[46]",
"numberOfBytes": "1472",
"label": "uint256[44]",
"numberOfBytes": "1408",
"base": "t_uint256"
},
"t_array(t_uint256)49_storage": {
@ -129,6 +145,11 @@
"numberOfBytes": "32",
"value": "t_bool"
},
"t_string_storage": {
"encoding": "bytes",
"label": "string",
"numberOfBytes": "32"
},
"t_uint256": {
"encoding": "inplace",
"label": "uint256",

View file

@ -261,7 +261,8 @@ contract AVSDeployer is Test {
avsOwner,
rewardsInitiator,
validatorsStrategies,
address(snowbridgeGatewayMock)
address(snowbridgeGatewayMock),
"v-mock"
)
)
)