1+ // SPDX-License-Identifier: BUSL-1.1
2+ pragma solidity ^ 0.8.12 ;
3+
4+ import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol " ;
5+ import {ISignatureUtils} from "src/contracts/interfaces/ISignatureUtils.sol " ;
6+ import {IAVSDirectory} from "src/contracts/interfaces/IAVSDirectory.sol " ;
7+ import {IRewardsCoordinator} from "src/contracts/interfaces/IRewardsCoordinator.sol " ;
8+
9+ /**
10+ * @title Minimal implementation of a ServiceManager-type contract.
11+ * This contract can be inherited from or simply used as a point-of-reference.
12+ * @author Layr Labs, Inc.
13+ */
14+ contract ServiceManagerMock is OwnableUpgradeable {
15+
16+ IAVSDirectory internal immutable _avsDirectory;
17+ IRewardsCoordinator internal immutable _rewardsCoordinator;
18+
19+ address [] public restakeableStrategies;
20+ mapping (address => address []) public operatorRestakedStrategies;
21+
22+ /// @notice Sets the (immutable) `_registryCoordinator` address
23+ constructor (
24+ IAVSDirectory __avsDirectory ,
25+ IRewardsCoordinator ___rewardsCoordinator
26+ ) {
27+ _avsDirectory = __avsDirectory;
28+ _rewardsCoordinator = ___rewardsCoordinator;
29+ _disableInitializers ();
30+ }
31+
32+ function initialize (address _initialOwner ) public initializer {
33+ _transferOwnership (_initialOwner);
34+ }
35+
36+ function __ServiceManagerBase_init (address initialOwner ) internal virtual onlyInitializing {
37+ _transferOwnership (initialOwner);
38+ }
39+
40+ /**
41+ * @notice Updates the metadata URI for the AVS
42+ * @param _metadataURI is the metadata URI for the AVS
43+ * @dev only callable by the owner
44+ */
45+ function updateAVSMetadataURI (string memory _metadataURI ) public virtual onlyOwner {
46+ _avsDirectory.updateAVSMetadataURI (_metadataURI);
47+ }
48+
49+ /**
50+ * @notice Creates a new range payment on behalf of an AVS, to be split amongst the
51+ * set of stakers delegated to operators who are registered to the `avs`.
52+ * Note that the owner calling this function must have approved the tokens to be transferred to the ServiceManager
53+ * and of course has the required balances.
54+ * @param rewardsSubmissions The range payments being created
55+ * @dev Expected to be called by the ServiceManager of the AVS on behalf of which the payment is being made
56+ * @dev The duration of the `rangePayment` cannot exceed `rewardsCoordinator.MAX_PAYMENT_DURATION()`
57+ * @dev The tokens are sent to the `PaymentCoordinator` contract
58+ * @dev Strategies must be in ascending order of addresses to check for duplicates
59+ * @dev This function will revert if the `rangePayment` is malformed,
60+ * e.g. if the `strategies` and `weights` arrays are of non-equal lengths
61+ */
62+ function createAVSRewardsSubmission (
63+ IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions
64+ ) public virtual onlyOwner {
65+ for (uint256 i = 0 ; i < rewardsSubmissions.length ; ++ i) {
66+ // transfer token to ServiceManager and approve PaymentCoordinator to transfer again
67+ // in createAVSRewardsSubmission() call
68+ rewardsSubmissions[i].token.transferFrom (msg .sender , address (this ), rewardsSubmissions[i].amount);
69+ uint256 allowance = rewardsSubmissions[i].token.allowance (address (this ), address (_rewardsCoordinator));
70+ rewardsSubmissions[i].token.approve (address (_rewardsCoordinator), rewardsSubmissions[i].amount + allowance);
71+ }
72+
73+ _rewardsCoordinator.createAVSRewardsSubmission (rewardsSubmissions);
74+ }
75+
76+ function createOperatorDirectedAVSRewardsSubmission (
77+ IRewardsCoordinator.OperatorDirectedRewardsSubmission[] calldata rewardsSubmissions
78+ ) public virtual onlyOwner {
79+ for (uint256 i = 0 ; i < rewardsSubmissions.length ; ++ i) {
80+ uint256 amount = 0 ;
81+ for (uint256 j = 0 ; j < rewardsSubmissions[i].operatorRewards.length ; ++ j) {
82+ amount += rewardsSubmissions[i].operatorRewards[j].amount;
83+ }
84+ rewardsSubmissions[i].token.transferFrom (msg .sender , address (this ), amount);
85+ uint256 allowance = rewardsSubmissions[i].token.allowance (address (this ), address (_rewardsCoordinator));
86+ rewardsSubmissions[i].token.approve (address (_rewardsCoordinator), amount + allowance);
87+ }
88+
89+ _rewardsCoordinator.createOperatorDirectedAVSRewardsSubmission (address (this ), rewardsSubmissions);
90+ }
91+
92+ /**
93+ * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS
94+ * @param operator The address of the operator to register.
95+ * @param operatorSignature The signature, salt, and expiry of the operator's signature.
96+ */
97+ function registerOperatorToAVS (
98+ address operator ,
99+ ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
100+ ) public virtual {
101+ _avsDirectory.registerOperatorToAVS (operator, operatorSignature);
102+ }
103+
104+ /**
105+ * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS
106+ * @param operator The address of the operator to deregister.
107+ */
108+ function deregisterOperatorFromAVS (address operator ) public virtual {
109+ _avsDirectory.deregisterOperatorFromAVS (operator);
110+ }
111+
112+ /**
113+ * @notice Returns the list of strategies that the AVS supports for restaking
114+ * @dev This function is intended to be called off-chain
115+ * @dev No guarantee is made on uniqueness of each element in the returned array.
116+ * The off-chain service should do that validation separately
117+ */
118+ function getRestakeableStrategies () external view returns (address [] memory ) {
119+ return restakeableStrategies;
120+ }
121+
122+ /**
123+ * @notice Returns the list of strategies that the operator has potentially restaked on the AVS
124+ * @param operator The address of the operator to get restaked strategies for
125+ * @dev This function is intended to be called off-chain
126+ * @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness
127+ * of each element in the returned array. The off-chain service should do that validation separately
128+ */
129+ function getOperatorRestakedStrategies (address operator ) external view returns (address [] memory ) {
130+ return operatorRestakedStrategies[operator];
131+ }
132+
133+ function setRestakeableStrategies (address [] memory strategies ) external {
134+ delete restakeableStrategies;
135+ for (uint256 i = 0 ; i < strategies.length ; ++ i) {
136+ restakeableStrategies.push (strategies[i]);
137+ }
138+ }
139+
140+ function setOperatorRestakedStrategies (address operator , address [] memory strategies ) external {
141+ delete operatorRestakedStrategies[operator];
142+ for (uint256 i = 0 ; i < strategies.length ; ++ i) {
143+ operatorRestakedStrategies[operator].push (strategies[i]);
144+ }
145+ }
146+
147+ /// @notice Returns the EigenLayer AVSDirectory contract.
148+ function avsDirectory () external view returns (address ) {
149+ return address (_avsDirectory);
150+ }
151+
152+ function rewardsCoordinator () external view returns (address ) {
153+ return address (_rewardsCoordinator);
154+ }
155+
156+ // storage gap for upgradeability
157+ // slither-disable-next-line shadowing-state
158+ uint256 [48 ] private __GAP;
159+ }
0 commit comments