Skip to content

Commit

Permalink
Feat: enumerating sets operators are in (#662)
Browse files Browse the repository at this point in the history
* feat: track sets operators in

* feat: add natspec

* refactor: reorganize

* fix: tests passing

* fix: compile warnings

* fix: test passing

* feat: add `operatorSetsMemberOf` pagination

* test: add coverage

* chore: bindings

---------

Co-authored-by: Yash Patil <[email protected]>
  • Loading branch information
2 people authored and 8sunyuan committed Aug 15, 2024
1 parent 8b162d7 commit 13a00e7
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 89 deletions.
121 changes: 107 additions & 14 deletions pkg/bindings/AVSDirectory/binding.go

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions pkg/bindings/AVSDirectoryStorage/binding.go

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions pkg/bindings/IAVSDirectory/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/RewardsCoordinator/binding.go

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions script/deploy/mainnet/Deploy_Strategy_Factory.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "@openzeppelin/contracts/utils/Create2.sol";
import "../../utils/ExistingDeploymentParser.sol";

import "../../../src/contracts/strategies/StrategyFactory.sol";

/**
* @notice Script used for the first deployment of EigenLayer core contracts to Holesky
* FORK LOCAL
* anvil --fork-url $RPC_MAINNET
* forge script script/deploy/mainnet/Deploy_Strategy_Factory.s.sol:MainnetStrategyFactoryDeploy --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv
*
* MAINNET
* forge script script/deploy/mainnet/Deploy_Strategy_Factory.s.sol:MainnetStrategyFactoryDeploy --rpc-url $RPC_MAINNET --private-key $PRIVATE_KEY --verify --broadcast -vvvv
*
*/

contract MainnetStrategyFactoryDeploy is ExistingDeploymentParser {
function run() external virtual {
// Use rewards config
_parseInitialDeploymentParams(
"script/configs/mainnet/v0.3.0-mainnet-rewards.config.json"
);
_parseDeployedContracts(
"script/configs/mainnet/Mainnet_curent_deployment.config.json"
);

// START RECORDING TRANSACTIONS FOR DEPLOYMENT
vm.startBroadcast();

emit log_named_address("Deployer Address", msg.sender);

_deployStrategyFactory();

// STOP RECORDING TRANSACTIONS FOR DEPLOYMENT
vm.stopBroadcast();

// Sanity Checks
_verifyContractPointers();
_verifyImplementations();
_verifyContractsInitialized({isInitialDeployment: true});
_verifyInitializationParams();

logAndOutputContractAddresses("script/output/mainnet/v0.3.2-mainnet-strategy-factory.output.json");
}

/**
* @notice Deploy StrategyFactory for Mainnet
*/

function _deployStrategyFactory() internal {
strategyFactoryImplementation = new StrategyFactory(
strategyManager
);



}
}
77 changes: 67 additions & 10 deletions src/contracts/core/AVSDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.12;
import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import "@openzeppelin-upgrades/contracts/security/ReentrancyGuardUpgradeable.sol";

import "../permissions/Pausable.sol";
import "../libraries/EIP1271SignatureUtils.sol";
import "./AVSDirectoryStorage.sol";
Expand All @@ -15,6 +16,8 @@ contract AVSDirectory is
AVSDirectoryStorage,
ReentrancyGuardUpgradeable
{
using EnumerableSet for EnumerableSet.Bytes32Set;

/// @dev Index for flag that pauses operator register/deregister to avs when set.
uint8 internal constant PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS = 0;
/// @dev Index for flag that pauses operator register/deregister to operator sets when set.
Expand Down Expand Up @@ -371,23 +374,23 @@ contract AVSDirectory is
function _registerToOperatorSets(address operator, address avs, uint32[] calldata operatorSetIds) internal {
// Loop over `operatorSetIds` array and register `operator` for each item.
for (uint256 i = 0; i < operatorSetIds.length; ++i) {
OperatorSet memory operatorSet = OperatorSet(avs, operatorSetIds[i]);

require(
isOperatorSet[avs][operatorSetIds[i]],
"AVSDirectory._registerOperatorToOperatorSets: invalid operator set"
);

// Assert `operator` has not already been registered to `operatorSetIds[i]`.
require(
!isMember[avs][operator][operatorSetIds[i]],
!isMember(operator, operatorSet),
"AVSDirectory._registerOperatorToOperatorSets: operator already registered to operator set"
);

++operatorSetMemberCount[avs][operatorSetIds[i]];

// Mutate `isMember` to `true`.
isMember[avs][operator][operatorSetIds[i]] = true;
_operatorSetsMemberOf[operator].add(_encodeOperatorSet(operatorSet));

emit OperatorAddedToOperatorSet(operator, OperatorSet({avs: avs, operatorSetId: operatorSetIds[i]}));
emit OperatorAddedToOperatorSet(operator, operatorSet);
}
}

Expand All @@ -401,18 +404,18 @@ contract AVSDirectory is
function _deregisterFromOperatorSets(address avs, address operator, uint32[] calldata operatorSetIds) internal {
// Loop over `operatorSetIds` array and deregister `operator` for each item.
for (uint256 i = 0; i < operatorSetIds.length; ++i) {
// Assert `operator` is registered for this iterations operator set.
OperatorSet memory operatorSet = OperatorSet(avs, operatorSetIds[i]);

require(
isMember[avs][operator][operatorSetIds[i]],
isMember(operator, operatorSet),
"AVSDirectory._deregisterOperatorFromOperatorSet: operator not registered for operator set"
);

--operatorSetMemberCount[avs][operatorSetIds[i]];

// Mutate `isMember` to `false`.
isMember[avs][operator][operatorSetIds[i]] = false;
_operatorSetsMemberOf[operator].remove(_encodeOperatorSet(operatorSet));

emit OperatorRemovedFromOperatorSet(operator, OperatorSet({avs: avs, operatorSetId: operatorSetIds[i]}));
emit OperatorRemovedFromOperatorSet(operator, operatorSet);
}
}

Expand All @@ -422,6 +425,43 @@ contract AVSDirectory is
*
*/

/// @notice Returns operator sets an operator is registered to in the order they were registered.
/// @param operator The operator address to query.
/// @param index The index of the enumerated list of operator sets.
function operatorSetsMemberOf(address operator, uint256 index) public view returns (OperatorSet memory) {
return _decodeOperatorSet(_operatorSetsMemberOf[operator].at(index));
}

/// @notice Returns an array of operator sets an operator is registered to.
/// @param operator The operator address to query.
/// @param start The starting index of the array to query.
/// @param length The amount of items of the array to return.
function operatorSetsMemberOf(
address operator,
uint256 start,
uint256 length
) public view returns (OperatorSet[] memory operatorSets) {
uint256 maxLength = _operatorSetsMemberOf[operator].length() - start;
if (length > maxLength) length = maxLength;
operatorSets = new OperatorSet[](length);
for (uint256 i; i < length; ++i) {
operatorSets[i] = _decodeOperatorSet(_operatorSetsMemberOf[operator].at(start + i));
}
}

/// @notice Returns the total number of operator sets an operator is registered to.
/// @param operator The operator address to query.
function inTotalOperatorSets(address operator) public view returns (uint256) {
return _operatorSetsMemberOf[operator].length();
}

/// @notice Returns whether or not an operator is registered to an operator set.
/// @param operator The operator address to query.
/// @param operatorSet The `OperatorSet` to query.
function isMember(address operator, OperatorSet memory operatorSet) public view returns (bool) {
return _operatorSetsMemberOf[operator].contains(_encodeOperatorSet(operatorSet));
}

/**
* @notice Calculates the digest hash to be signed by an operator to register with an AVS.
*
Expand Down Expand Up @@ -493,7 +533,24 @@ contract AVSDirectory is
}
}

/// @notice Returns an EIP-712 encoded hash struct.
function _calculateDigestHash(bytes32 structHash) internal view returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", _calculateDomainSeparator(), structHash));
}

/// @dev Returns an `OperatorSet` encoded into a 32-byte value.
/// @param operatorSet The `OperatorSet` to encode.
function _encodeOperatorSet(OperatorSet memory operatorSet) internal pure returns (bytes32) {
return bytes32(abi.encodePacked(operatorSet.avs, uint96(operatorSet.operatorSetId)));
}

/// @dev Returns an `OperatorSet` decoded from an encoded 32-byte value.
/// @param encoded The encoded `OperatorSet` to decode.
/// @dev Assumes `encoded` is encoded via `_encodeOperatorSet(operatorSet)`.
function _decodeOperatorSet(bytes32 encoded) internal pure returns (OperatorSet memory) {
return OperatorSet({
avs: address(uint160(uint256(encoded) >> 96)),
operatorSetId: uint32(uint256(encoded) & type(uint96).max)
});
}
}
11 changes: 8 additions & 3 deletions src/contracts/core/AVSDirectoryStorage.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "../interfaces/IAVSDirectory.sol";
import "../interfaces/IDelegationManager.sol";

abstract contract AVSDirectoryStorage is IAVSDirectory {
using EnumerableSet for EnumerableSet.Bytes32Set;

/// @notice The EIP-712 typehash for the contract's domain
bytes32 public constant DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
Expand Down Expand Up @@ -44,12 +48,13 @@ abstract contract AVSDirectoryStorage is IAVSDirectory {
/// @notice Mapping: avs => operatorSetId => Whether or not an operator set is valid.
mapping(address => mapping(uint32 => bool)) public isOperatorSet;

/// @notice Mapping: avs = operator => operatorSetId => Whether or not an operator is a member of an operator set.
mapping(address => mapping(address => mapping(uint32 => bool))) public isMember;

/// @notice Mapping: avs => operatorSetId => Total operators within the given operator set.
mapping(address => mapping(uint32 => uint256)) public operatorSetMemberCount;

/// @notice Mapping: operator => List of operator sets that operator is registered to.
/// @dev Each item is formatted as such: bytes32(abi.encodePacked(avs, uint96(operatorSetId)))
mapping(address => EnumerableSet.Bytes32Set) internal _operatorSetsMemberOf;

constructor(IDelegationManager _delegation) {
delegation = _delegation;
}
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/interfaces/IAVSDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ interface IAVSDirectory is ISignatureUtils {
*/
function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool);

function isMember(address avs, address operator, uint32 operatorSetId) external view returns (bool);
function isMember(address operator, OperatorSet memory operatorSet) external view returns (bool);

function isOperatorSetAVS(address avs) external view returns (bool);

Expand Down
2 changes: 2 additions & 0 deletions src/test/mocks/AVSDirectoryMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,6 @@ contract AVSDirectoryMock is IAVSDirectory, Test {

/// @notice The EIP-712 typehash for the OperatorSetRegistration struct used by the contract.
function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32) {}

function isMember(address operator, IAVSDirectory.OperatorSet memory operatorSet) external view returns (bool) {}
}
Loading

0 comments on commit 13a00e7

Please sign in to comment.