mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
In this PR:
1. Implement application-specific functionalities in the
`DataHavenServiceManager` contract:
1. Registering of 3 operator sets: Validators, BSPs and MSPs.
2. Allowlisted sign up of operators.
3. Integration with Snowbridge to send message of new validator set.
2. Basic testing of the above functionalities.
3. Tests now use less mocked contracts (especially from EigenLayer).
4. Refactor of `SignUpOperator` script, which now supports the three
kinds of Operator sets.
421 lines
18 KiB
Solidity
421 lines
18 KiB
Solidity
// SPDX-License-Identifier: BUSL-1.1
|
|
pragma solidity ^0.8.27;
|
|
|
|
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
|
|
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
|
import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol";
|
|
import {IAllocationManagerTypes} from
|
|
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
|
|
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
|
|
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
|
|
import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol";
|
|
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
|
|
import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol";
|
|
import {PermissionController} from
|
|
"eigenlayer-contracts/src/contracts/permissions/PermissionController.sol";
|
|
import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol";
|
|
import {
|
|
IRewardsCoordinator,
|
|
IRewardsCoordinatorTypes
|
|
} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
|
|
import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol";
|
|
import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol";
|
|
|
|
import {ERC20FixedSupply} from "./ERC20FixedSupply.sol";
|
|
import {IServiceManager} from "../../src/interfaces/IServiceManager.sol";
|
|
import {VetoableSlasher} from "../../src/middleware/VetoableSlasher.sol";
|
|
import {IVetoableSlasher} from "../../src/interfaces/IVetoableSlasher.sol";
|
|
import {RewardsRegistry} from "../../src/middleware/RewardsRegistry.sol";
|
|
import {DataHavenServiceManager} from "../../src/DataHavenServiceManager.sol";
|
|
// Mocks
|
|
import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol";
|
|
import {RewardsCoordinatorMock} from "../mocks/RewardsCoordinatorMock.sol";
|
|
import {PermissionControllerMock} from "../mocks/PermissionControllerMock.sol";
|
|
import {EigenPodManagerMock} from "../mocks/EigenPodManagerMock.sol";
|
|
import {AllocationManagerMock} from "../mocks/AllocationManagerMock.sol";
|
|
import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol";
|
|
|
|
import "forge-std/Test.sol";
|
|
|
|
contract AVSDeployer is Test {
|
|
Vm public cheats = Vm(VM_ADDRESS);
|
|
|
|
ProxyAdmin public proxyAdmin;
|
|
PauserRegistry public pauserRegistry;
|
|
|
|
EmptyContract public emptyContract;
|
|
|
|
// AVS contracts
|
|
DataHavenServiceManager public serviceManager;
|
|
DataHavenServiceManager public serviceManagerImplementation;
|
|
VetoableSlasher public vetoableSlasher;
|
|
RewardsRegistry public rewardsRegistry;
|
|
|
|
// VetoableSlasher roles and parameters
|
|
address public vetoCommitteeMember = address(uint160(uint256(keccak256("vetoCommitteeMember"))));
|
|
uint32 public vetoWindowBlocks = 100; // 100 blocks veto window for tests
|
|
|
|
// RewardsRegistry roles and parameters
|
|
address public mockRewardsAgent = address(uint160(uint256(keccak256("rewardsAgent"))));
|
|
|
|
// EigenLayer contracts
|
|
StrategyManager public strategyManager;
|
|
StrategyManager public strategyManagerImplementation;
|
|
DelegationManager public delegationManager;
|
|
DelegationManager public delegationManagerImplementation;
|
|
EigenPodManagerMock public eigenPodManagerMock;
|
|
AllocationManager public allocationManager;
|
|
AllocationManager public allocationManagerImplementation;
|
|
RewardsCoordinator public rewardsCoordinator;
|
|
RewardsCoordinator public rewardsCoordinatorImplementation;
|
|
RewardsCoordinatorMock public rewardsCoordinatorMock;
|
|
PermissionControllerMock public permissionControllerMock;
|
|
|
|
// Addresses
|
|
address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner"))));
|
|
address public regularDeployer = address(uint160(uint256(keccak256("regularDeployer"))));
|
|
address public avsOwner = address(uint160(uint256(keccak256("avsOwner"))));
|
|
address public rewardsInitiator = address(uint160(uint256(keccak256("rewardsInitiator"))));
|
|
address public pauser = address(uint160(uint256(keccak256("pauser"))));
|
|
address public unpauser = address(uint160(uint256(keccak256("unpauser"))));
|
|
address public rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater"))));
|
|
address public strategyOwner = address(uint160(uint256(keccak256("strategyOwner"))));
|
|
|
|
// RewardsCoordinator constants
|
|
uint32 public constant CALCULATION_INTERVAL_SECONDS = 7 days;
|
|
uint32 public constant MAX_REWARDS_DURATION = 70 days;
|
|
uint32 public constant MAX_RETROACTIVE_LENGTH = 84 days;
|
|
uint32 public constant MAX_FUTURE_LENGTH = 28 days;
|
|
uint32 public constant GENESIS_REWARDS_TIMESTAMP = 1712188800;
|
|
|
|
/// @notice Delay in timestamp before a posted root can be claimed against
|
|
uint32 public activationDelay = 7 days;
|
|
/// @notice the commission for all operators across all AVSs
|
|
uint16 public globalCommissionBips = 1000;
|
|
|
|
// Mock strategies
|
|
IERC20[] public rewardTokens;
|
|
uint256 public mockTokenInitialSupply = 10e50;
|
|
IStrategy[] public deployedStrategies;
|
|
StrategyBase public strategyImplementation;
|
|
IRewardsCoordinator.StrategyAndMultiplier[] public defaultStrategyAndMultipliers;
|
|
|
|
function _deployMockEigenLayerAndAVS() internal {
|
|
emptyContract = new EmptyContract();
|
|
|
|
// Deploy EigenLayer core contracts.
|
|
cheats.startPrank(proxyAdminOwner);
|
|
proxyAdmin = new ProxyAdmin();
|
|
cheats.stopPrank();
|
|
|
|
console.log("ProxyAdmin deployed");
|
|
|
|
cheats.startPrank(regularDeployer);
|
|
address[] memory pausers = new address[](1);
|
|
pausers[0] = pauser;
|
|
pauserRegistry = new PauserRegistry(pausers, unpauser);
|
|
eigenPodManagerMock = new EigenPodManagerMock(pauserRegistry);
|
|
permissionControllerMock = new PermissionControllerMock();
|
|
rewardsCoordinatorMock = new RewardsCoordinatorMock();
|
|
cheats.stopPrank();
|
|
|
|
console.log("Mock EigenLayer contracts deployed");
|
|
|
|
// Deploying proxy contracts for AllocationManager and StrategyManager.
|
|
// The `proxyAdmin` contract is set as the admin of the proxy contracts,
|
|
// which will be later upgraded to the actual implementation.
|
|
cheats.prank(regularDeployer);
|
|
allocationManager = AllocationManager(
|
|
address(
|
|
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
|
|
)
|
|
);
|
|
strategyManager = StrategyManager(
|
|
address(
|
|
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
|
|
)
|
|
);
|
|
|
|
console.log("AllocationManager and StrategyManager proxy contracts deployed");
|
|
|
|
// Deploying DelegationManager implementation and its proxy.
|
|
cheats.prank(regularDeployer);
|
|
delegationManagerImplementation = new DelegationManager(
|
|
strategyManager,
|
|
eigenPodManagerMock,
|
|
allocationManager,
|
|
pauserRegistry,
|
|
permissionControllerMock,
|
|
uint32(10), // MIN_WITHDRAWAL_DELAY_BLOCKS
|
|
"v-mock"
|
|
);
|
|
cheats.prank(regularDeployer);
|
|
delegationManager = DelegationManager(
|
|
address(
|
|
new TransparentUpgradeableProxy(
|
|
address(delegationManagerImplementation), address(proxyAdmin), ""
|
|
)
|
|
)
|
|
);
|
|
// Deploying AllocationManager implementation and upgrading the proxy.
|
|
cheats.prank(regularDeployer);
|
|
allocationManagerImplementation = new AllocationManager(
|
|
delegationManager,
|
|
pauserRegistry,
|
|
permissionControllerMock,
|
|
uint32(7 days), // DEALLOCATION_DELAY
|
|
uint32(1 days), // ALLOCATION_CONFIGURATION_DELAY
|
|
"v-mock"
|
|
);
|
|
cheats.prank(proxyAdminOwner);
|
|
proxyAdmin.upgrade(
|
|
ITransparentUpgradeableProxy(address(allocationManager)),
|
|
address(allocationManagerImplementation)
|
|
);
|
|
|
|
console.log("AllocationManager implementation deployed");
|
|
|
|
// Deploying StrategyManager implementation and its proxy.
|
|
cheats.prank(regularDeployer);
|
|
strategyManagerImplementation =
|
|
new StrategyManager(delegationManager, pauserRegistry, "v-mock");
|
|
cheats.prank(proxyAdminOwner);
|
|
uint256 allUnpaused = 0;
|
|
proxyAdmin.upgradeAndCall(
|
|
ITransparentUpgradeableProxy(address(strategyManager)),
|
|
address(strategyManagerImplementation),
|
|
abi.encodeWithSelector(
|
|
StrategyManager.initialize.selector, strategyOwner, strategyOwner, allUnpaused
|
|
)
|
|
);
|
|
|
|
console.log("StrategyManager implementation deployed");
|
|
|
|
// Deploying RewardsCoordinator implementation and its proxy.
|
|
// When the proxy is deployed, the `initialize` function is called.
|
|
cheats.startPrank(regularDeployer);
|
|
IRewardsCoordinatorTypes.RewardsCoordinatorConstructorParams memory params =
|
|
IRewardsCoordinatorTypes.RewardsCoordinatorConstructorParams({
|
|
delegationManager: delegationManager,
|
|
strategyManager: IStrategyManager(address(strategyManager)),
|
|
allocationManager: allocationManager,
|
|
pauserRegistry: pauserRegistry,
|
|
permissionController: permissionControllerMock,
|
|
CALCULATION_INTERVAL_SECONDS: CALCULATION_INTERVAL_SECONDS,
|
|
MAX_REWARDS_DURATION: MAX_REWARDS_DURATION,
|
|
MAX_RETROACTIVE_LENGTH: MAX_RETROACTIVE_LENGTH,
|
|
MAX_FUTURE_LENGTH: MAX_FUTURE_LENGTH,
|
|
GENESIS_REWARDS_TIMESTAMP: GENESIS_REWARDS_TIMESTAMP,
|
|
version: "v-mock"
|
|
});
|
|
rewardsCoordinatorImplementation = new RewardsCoordinator(params);
|
|
rewardsCoordinator = RewardsCoordinator(
|
|
address(
|
|
new TransparentUpgradeableProxy(
|
|
address(rewardsCoordinatorImplementation),
|
|
address(proxyAdmin),
|
|
abi.encodeWithSelector(
|
|
RewardsCoordinator.initialize.selector,
|
|
msg.sender,
|
|
0, /*initialPausedStatus*/
|
|
rewardsUpdater,
|
|
activationDelay,
|
|
globalCommissionBips
|
|
)
|
|
)
|
|
)
|
|
);
|
|
cheats.stopPrank();
|
|
|
|
console.log("RewardsCoordinator implementation deployed");
|
|
|
|
// Set up strategies before deploying the ServiceManager
|
|
_setUpDefaultStrategiesAndMultipliers();
|
|
|
|
// Deploying ServiceManager implementation and its proxy.
|
|
// When the proxy is deployed, the `initialize` function is called.
|
|
cheats.startPrank(regularDeployer);
|
|
serviceManagerImplementation = new DataHavenServiceManager(
|
|
rewardsCoordinator, permissionControllerMock, allocationManager
|
|
);
|
|
|
|
// Create arrays for the three sets of strategies required by DataHavenServiceManager
|
|
IStrategy[] memory validatorsStrategies = new IStrategy[](deployedStrategies.length);
|
|
IStrategy[] memory bspsStrategies = new IStrategy[](deployedStrategies.length);
|
|
IStrategy[] memory mspsStrategies = new IStrategy[](deployedStrategies.length);
|
|
|
|
// For testing purposes, we'll use the same strategies for all three sets
|
|
for (uint256 i = 0; i < deployedStrategies.length; i++) {
|
|
validatorsStrategies[i] = deployedStrategies[i];
|
|
bspsStrategies[i] = deployedStrategies[i];
|
|
mspsStrategies[i] = deployedStrategies[i];
|
|
}
|
|
|
|
serviceManager = DataHavenServiceManager(
|
|
address(
|
|
new TransparentUpgradeableProxy(
|
|
address(serviceManagerImplementation),
|
|
address(proxyAdmin),
|
|
abi.encodeWithSelector(
|
|
DataHavenServiceManager.initialise.selector,
|
|
avsOwner,
|
|
rewardsInitiator,
|
|
validatorsStrategies,
|
|
bspsStrategies,
|
|
mspsStrategies,
|
|
address(0) // This deployment does not use Snowbridge
|
|
)
|
|
)
|
|
)
|
|
);
|
|
cheats.stopPrank();
|
|
console.log("ServiceManager implementation deployed");
|
|
|
|
// Deploy and configure the VetoableSlasher
|
|
cheats.prank(regularDeployer);
|
|
vetoableSlasher = new VetoableSlasher(
|
|
allocationManager, serviceManager, vetoCommitteeMember, vetoWindowBlocks
|
|
);
|
|
|
|
// Set the slasher in the ServiceManager
|
|
cheats.prank(avsOwner);
|
|
serviceManager.setSlasher(vetoableSlasher);
|
|
|
|
console.log("VetoableSlasher deployed and configured");
|
|
|
|
// Deploy the RewardsRegistry contract
|
|
cheats.prank(regularDeployer);
|
|
rewardsRegistry = new RewardsRegistry(address(serviceManager), mockRewardsAgent);
|
|
|
|
// Set the rewards registry in the ServiceManager
|
|
cheats.prank(avsOwner);
|
|
serviceManager.setRewardsRegistry(0, rewardsRegistry);
|
|
|
|
console.log("RewardsRegistry deployed and configured");
|
|
}
|
|
|
|
function _setUpDefaultStrategiesAndMultipliers() internal virtual {
|
|
// Deploy mock tokens to be used for strategies.
|
|
cheats.startPrank(strategyOwner);
|
|
IERC20 token1 =
|
|
new ERC20FixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, address(this));
|
|
IERC20 token2 =
|
|
new ERC20FixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this));
|
|
IERC20 token3 =
|
|
new ERC20FixedSupply("pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this));
|
|
|
|
// Deploy mock strategies.
|
|
strategyImplementation =
|
|
new StrategyBase(IStrategyManager(address(strategyManager)), pauserRegistry, "v-mock");
|
|
deployedStrategies.push(
|
|
StrategyBase(
|
|
address(
|
|
new TransparentUpgradeableProxy(
|
|
address(strategyImplementation),
|
|
address(proxyAdmin),
|
|
abi.encodeWithSelector(
|
|
StrategyBase.initialize.selector, token1, pauserRegistry
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
deployedStrategies.push(
|
|
StrategyBase(
|
|
address(
|
|
new TransparentUpgradeableProxy(
|
|
address(strategyImplementation),
|
|
address(proxyAdmin),
|
|
abi.encodeWithSelector(
|
|
StrategyBase.initialize.selector, token2, pauserRegistry
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
deployedStrategies.push(
|
|
StrategyBase(
|
|
address(
|
|
new TransparentUpgradeableProxy(
|
|
address(strategyImplementation),
|
|
address(proxyAdmin),
|
|
abi.encodeWithSelector(
|
|
StrategyBase.initialize.selector, token3, pauserRegistry
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
cheats.stopPrank();
|
|
|
|
deployedStrategies = _sortArrayAsc(deployedStrategies);
|
|
|
|
cheats.startPrank(strategyOwner);
|
|
strategyManager.addStrategiesToDepositWhitelist(deployedStrategies);
|
|
cheats.stopPrank();
|
|
|
|
defaultStrategyAndMultipliers.push(
|
|
IRewardsCoordinatorTypes.StrategyAndMultiplier(
|
|
IStrategy(address(deployedStrategies[0])), 1e18
|
|
)
|
|
);
|
|
defaultStrategyAndMultipliers.push(
|
|
IRewardsCoordinatorTypes.StrategyAndMultiplier(
|
|
IStrategy(address(deployedStrategies[1])), 2e18
|
|
)
|
|
);
|
|
defaultStrategyAndMultipliers.push(
|
|
IRewardsCoordinatorTypes.StrategyAndMultiplier(
|
|
IStrategy(address(deployedStrategies[2])), 3e18
|
|
)
|
|
);
|
|
}
|
|
|
|
function _labelContracts() internal {
|
|
cheats.label(address(emptyContract), "EmptyContract");
|
|
cheats.label(address(proxyAdmin), "ProxyAdmin");
|
|
cheats.label(address(pauserRegistry), "PauserRegistry");
|
|
cheats.label(address(delegationManager), "DelegationManager");
|
|
cheats.label(address(eigenPodManagerMock), "EigenPodManagerMock");
|
|
cheats.label(address(strategyManager), "StrategyManager");
|
|
cheats.label(address(rewardsCoordinatorMock), "RewardsCoordinatorMock");
|
|
cheats.label(address(allocationManager), "AllocationManager");
|
|
cheats.label(address(allocationManagerImplementation), "AllocationManagerImplementation");
|
|
cheats.label(address(serviceManager), "ServiceManager");
|
|
cheats.label(address(serviceManagerImplementation), "ServiceManagerImplementation");
|
|
cheats.label(address(vetoableSlasher), "VetoableSlasher");
|
|
}
|
|
|
|
/// @dev Sort to ensure that the array is in ascending order for strategies
|
|
function _sortArrayAsc(
|
|
IStrategy[] memory arr
|
|
) internal pure returns (IStrategy[] memory) {
|
|
uint256 l = arr.length;
|
|
for (uint256 i = 0; i < l; i++) {
|
|
for (uint256 j = i + 1; j < l; j++) {
|
|
if (address(arr[i]) > address(arr[j])) {
|
|
IStrategy temp = arr[i];
|
|
arr[i] = arr[j];
|
|
arr[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
function _incrementAddress(address start, uint256 inc) internal pure returns (address) {
|
|
return address(uint160(uint256(uint160(start) + inc)));
|
|
}
|
|
|
|
function _incrementBytes32(bytes32 start, uint256 inc) internal pure returns (bytes32) {
|
|
return bytes32(uint256(start) + inc);
|
|
}
|
|
|
|
function _setERC20Balance(address token, address user, uint256 amount) internal {
|
|
// Assumes balanceOf is in slot 0 (standard in OpenZeppelin ERC20)
|
|
bytes32 slot = keccak256(abi.encode(user, uint256(0)));
|
|
cheats.store(token, slot, bytes32(amount));
|
|
}
|
|
}
|