Skip to content

Commit

Permalink
fix: correct index get operator restakable strategies (#280)
Browse files Browse the repository at this point in the history
* fix: add unit tests for ECDSAServiceManager and fix index in getOperatorRestakableStrategies

* chore: formatter

* chore: formatter
  • Loading branch information
stevennevins authored Jul 1, 2024
1 parent d643f3b commit c00aad7
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 7 deletions.
24 changes: 17 additions & 7 deletions src/unaudited/ECDSAServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces
import {Quorum} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol";
import {ECDSAStakeRegistry} from "../unaudited/ECDSAStakeRegistry.sol";

abstract contract ECDSAServiceManagerBase is IServiceManager, OwnableUpgradeable {
abstract contract ECDSAServiceManagerBase is
IServiceManager,
OwnableUpgradeable
{
/// @notice Address of the stake registry contract, which manages registration and stake recording.
address public immutable stakeRegistry;

Expand Down Expand Up @@ -185,15 +188,19 @@ abstract contract ECDSAServiceManagerBase is IServiceManager, OwnableUpgradeable
address(this),
rewardsSubmissions[i].amount
);
uint256 allowance =
rewardsSubmissions[i].token.allowance(address(this), rewardsCoordinator);
uint256 allowance = rewardsSubmissions[i].token.allowance(
address(this),
rewardsCoordinator
);
rewardsSubmissions[i].token.approve(
rewardsCoordinator,
rewardsSubmissions[i].amount + allowance
);
}

IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(rewardsSubmissions);
IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(
rewardsSubmissions
);
}

/**
Expand Down Expand Up @@ -234,7 +241,6 @@ abstract contract ECDSAServiceManagerBase is IServiceManager, OwnableUpgradeable
uint256[] memory shares = IDelegationManager(delegationManager)
.getOperatorShares(_operator, strategies);

address[] memory activeStrategies = new address[](count);
uint256 activeCount;
for (uint256 i; i < count; i++) {
if (shares[i] > 0) {
Expand All @@ -244,9 +250,11 @@ abstract contract ECDSAServiceManagerBase is IServiceManager, OwnableUpgradeable

// Resize the array to fit only the active strategies
address[] memory restakedStrategies = new address[](activeCount);
uint256 index;
for (uint256 j = 0; j < count; j++) {
if (shares[j] > 0) {
restakedStrategies[j] = activeStrategies[j];
restakedStrategies[index] = address(strategies[j]);
index++;
}
}

Expand All @@ -258,7 +266,9 @@ abstract contract ECDSAServiceManagerBase is IServiceManager, OwnableUpgradeable
* @param newRewardsInitiator The new rewards initiator address.
* @dev Only callable by the owner.
*/
function setRewardsInitiator(address newRewardsInitiator) external onlyOwner {
function setRewardsInitiator(
address newRewardsInitiator
) external onlyOwner {
_setRewardsInitiator(newRewardsInitiator);
}

Expand Down
22 changes: 22 additions & 0 deletions test/mocks/ECDSAServiceManagerMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "../../src/unaudited/ECDSAServiceManagerBase.sol";

contract ECDSAServiceManagerMock is ECDSAServiceManagerBase {
constructor(
address _avsDirectory,
address _stakeRegistry,
address _rewardsCoordinator,
address _delegationManager
)
ECDSAServiceManagerBase(_avsDirectory, _stakeRegistry, _rewardsCoordinator, _delegationManager)
{}

function initialize(
address initialOwner,
address rewardsInitiator
) public virtual initializer {
__ServiceManagerBase_init(initialOwner, rewardsInitiator);
}
}
14 changes: 14 additions & 0 deletions test/mocks/ECDSAStakeRegistryMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "../../src/unaudited/ECDSAStakeRegistry.sol";

/**
* @title Mock for ECDSAStakeRegistry
* @dev This contract is a mock implementation of the ECDSAStakeRegistry for testing purposes.
*/
contract ECDSAStakeRegistryMock is ECDSAStakeRegistry {

constructor(IDelegationManager _delegationManager) ECDSAStakeRegistry(_delegationManager) {
}
}
186 changes: 186 additions & 0 deletions test/unit/ECDSAServiceManager.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

import {Test, console} from "forge-std/Test.sol";

import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";

import {ECDSAServiceManagerMock} from "../mocks/ECDSAServiceManagerMock.sol";
import {ECDSAStakeRegistryMock} from "../mocks/ECDSAStakeRegistryMock.sol";
import {Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol";

contract MockDelegationManager {
function operatorShares(address, address) external pure returns (uint256) {
return 1000; // Return a dummy value for simplicity
}

function getOperatorShares(
address,
IStrategy[] memory strategies
) external pure returns (uint256[] memory) {
uint256[] memory response = new uint256[](strategies.length);
for (uint256 i; i < strategies.length; i++) {
response[i] = 1000;
}
return response; // Return a dummy value for simplicity
}
}

contract MockAVSDirectory {
function registerOperatorToAVS(
address,
ISignatureUtils.SignatureWithSaltAndExpiry memory
) external pure {}

function deregisterOperatorFromAVS(address) external pure {}

function updateAVSMetadataURI(string memory) external pure {}
}

contract MockRewardsCoordinator {
function createAVSRewardsSubmission(
IRewardsCoordinator.RewardsSubmission[] calldata
) external pure {}
}

contract ECDSAServiceManagerSetup is Test {
MockDelegationManager public mockDelegationManager;
MockAVSDirectory public mockAVSDirectory;
ECDSAStakeRegistryMock public mockStakeRegistry;
MockRewardsCoordinator public mockRewardsCoordinator;
ECDSAServiceManagerMock public serviceManager;
address internal operator1;
address internal operator2;
uint256 internal operator1Pk;
uint256 internal operator2Pk;

function setUp() public {
mockDelegationManager = new MockDelegationManager();
mockAVSDirectory = new MockAVSDirectory();
mockStakeRegistry = new ECDSAStakeRegistryMock(
IDelegationManager(address(mockDelegationManager))
);
mockRewardsCoordinator = new MockRewardsCoordinator();

serviceManager = new ECDSAServiceManagerMock(
address(mockAVSDirectory),
address(mockStakeRegistry),
address(mockRewardsCoordinator),
address(mockDelegationManager)
);

operator1Pk = 1;
operator2Pk = 2;
operator1 = vm.addr(operator1Pk);
operator2 = vm.addr(operator2Pk);

// Create a quorum
Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)});
quorum.strategies[0] = StrategyParams({
strategy: IStrategy(address(420)),
multiplier: 5000
});
quorum.strategies[1] = StrategyParams({
strategy: IStrategy(address(421)),
multiplier: 5000
});
address[] memory operators = new address[](0);

vm.prank(mockStakeRegistry.owner());
mockStakeRegistry.initialize(
address(serviceManager),
10_000, // Assuming a threshold weight of 10000 basis points
quorum
);
ISignatureUtils.SignatureWithSaltAndExpiry memory dummySignature;

vm.prank(operator1);
mockStakeRegistry.registerOperatorWithSignature(
dummySignature,
operator1
);

vm.prank(operator2);
mockStakeRegistry.registerOperatorWithSignature(
dummySignature,
operator2
);
}

function testRegisterOperatorToAVS() public {
address operator = operator1;
ISignatureUtils.SignatureWithSaltAndExpiry memory signature;

vm.prank(address(mockStakeRegistry));
serviceManager.registerOperatorToAVS(operator, signature);
}

function testDeregisterOperatorFromAVS() public {
address operator = operator1;

vm.prank(address(mockStakeRegistry));
serviceManager.deregisterOperatorFromAVS(operator);
}

function testGetRestakeableStrategies() public {
address[] memory strategies = serviceManager.getRestakeableStrategies();
}

function testGetOperatorRestakedStrategies() public {
address operator = operator1;
address[] memory strategies = serviceManager
.getOperatorRestakedStrategies(operator);
}

function test_Regression_GetOperatorRestakedStrategies_NoShares() public {
address operator = operator1;
IStrategy[] memory strategies = new IStrategy[](2);
strategies[0] = IStrategy(address(420));
strategies[1] = IStrategy(address(421));

uint256[] memory shares = new uint256[](2);
shares[0] = 0;
shares[1] = 1;

vm.mockCall(
address(mockDelegationManager),
abi.encodeCall(
IDelegationManager.getOperatorShares,
(operator, strategies)
),
abi.encode(shares)
);

address[] memory restakedStrategies = serviceManager
.getOperatorRestakedStrategies(operator);
assertEq(
restakedStrategies.length,
1,
"Expected no restaked strategies"
);
}

function testUpdateAVSMetadataURI() public {
string memory newURI = "https://new-metadata-uri.com";

vm.prank(mockStakeRegistry.owner());
serviceManager.updateAVSMetadataURI(newURI);
}

function testCreateAVSRewardsSubmission() public {
IRewardsCoordinator.RewardsSubmission[] memory submissions;

vm.prank(serviceManager.rewardsInitiator());
serviceManager.createAVSRewardsSubmission(submissions);
}

function testSetRewardsInitiator() public {
address newInitiator = address(0x123);

vm.prank(mockStakeRegistry.owner());
serviceManager.setRewardsInitiator(newInitiator);
}
}

0 comments on commit c00aad7

Please sign in to comment.