Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Hyperlane AVS contracts #3651

Merged
merged 38 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c5ac704
forge install: eigenlayer-middleware
aroralanuk Apr 16, 2024
8873f20
install
aroralanuk Apr 22, 2024
2158670
getRestakeableStrategies
aroralanuk Apr 22, 2024
1ee6b10
comment
aroralanuk Apr 22, 2024
42ae9a6
enroll tc
aroralanuk Apr 23, 2024
325031e
add challenger
aroralanuk Apr 23, 2024
0488bc5
add enumerablemap
aroralanuk Apr 25, 2024
f737489
remove challengers
aroralanuk Apr 25, 2024
0eaffff
add unenroll tests
aroralanuk Apr 26, 2024
ef35202
fin testing
aroralanuk Apr 26, 2024
5c1c827
Merge branch 'main' into kunal/avs-contracts
aroralanuk Apr 26, 2024
a95a8c5
setSlasher
aroralanuk Apr 26, 2024
e348bdc
rm console.sol
aroralanuk Apr 26, 2024
5680d72
fix deregister bug
aroralanuk Apr 27, 2024
b8afaae
address yorke's comments
aroralanuk Apr 30, 2024
59c7019
Merge branch 'main' into kunal/avs-contracts
aroralanuk Apr 30, 2024
d7c2032
hardhat config filtering fail
aroralanuk Apr 30, 2024
01b1940
Revert "hardhat config filtering fail"
aroralanuk May 1, 2024
317d814
vendor interfaces fail
aroralanuk May 1, 2024
bf3e8df
vendor
aroralanuk May 4, 2024
421353e
Merge branch 'main' into kunal/avs-contracts
aroralanuk May 4, 2024
ff781c3
cleanup
aroralanuk May 4, 2024
05d7979
using steven's SMBase
aroralanuk May 6, 2024
df4a172
rm remapping upgrade
aroralanuk May 6, 2024
f045045
adding el core addresses
aroralanuk May 6, 2024
3abf566
Merge branch 'main' into kunal/avs-contracts
aroralanuk May 6, 2024
2ef337c
reading strategies from json
aroralanuk May 7, 2024
b50e0b2
add test for check man unenrollment
aroralanuk May 7, 2024
828f4e9
unenroll with address[]
aroralanuk May 7, 2024
7112431
function for single challenger enroll/unenroll
aroralanuk May 7, 2024
4cf5b02
separate vendored folder
aroralanuk May 7, 2024
7f3540b
add proxy deployment to forge
aroralanuk May 7, 2024
b159fdf
Merge branch 'main' into kunal/avs-contracts
aroralanuk May 7, 2024
23aa155
add global fuzz.runs config
aroralanuk May 7, 2024
e11ed99
update spelling
aroralanuk May 7, 2024
e62daa4
add incomp attribution to LL
aroralanuk May 7, 2024
b2e1643
deploy proxyAdmind
aroralanuk May 9, 2024
13f6da8
Merge branch 'main' into kunal/avs-contracts
aroralanuk May 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "solidity/lib/forge-std"]
path = solidity/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "solidity/lib/eigenlayer-middleware"]
path = solidity/lib/eigenlayer-middleware
url = https://github.com/Layr-Labs/eigenlayer-middleware
[submodule "solidity/lib/eigenlayer-contracts"]
path = solidity/lib/eigenlayer-contracts
url = https://github.com/Layr-Labs/eigenlayer-contracts
145 changes: 145 additions & 0 deletions solidity/contracts/avs/ECDSAServiceManagerBase.sol
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import {ISignatureUtils} from "@eigenlayer/interfaces/ISignatureUtils.sol";
import {IAVSDirectory} from "@eigenlayer/interfaces/IAVSDirectory.sol";
import {ISlasher} from "@eigenlayer/interfaces/ISlasher.sol";

import {ECDSAStakeRegistry} from "@eigenlayer/middleware/unaudited/ECDSAStakeRegistry.sol";
import {IServiceManager} from "@eigenlayer/middleware/interfaces/IServiceManager.sol";

import {IRemoteChallenger} from "../interfaces/avs/IRemoteChallenger.sol";

contract ECDSAServiceManagerBase is IServiceManager, OwnableUpgradeable {
event OperatorRegisteredToAVS(address indexed operator);
event OperatorDeregisteredToAVS(address indexed operator);

ECDSAStakeRegistry internal immutable stakeRegistry;
IAVSDirectory internal immutable elAvsDirectory;
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
ISlasher internal slasher;
Fixed Show fixed Hide fixed

/// @notice when applied to a function, only allows the ECDSAStakeRegistry to call it
modifier onlyStakeRegistry() {
require(
msg.sender == address(stakeRegistry),
"ECDSAServiceManagerBase: caller is not the stake registry"
);
_;
}

// ============ Constructor ============

constructor(
IAVSDirectory _avsDirectory,
ECDSAStakeRegistry _stakeRegistry,
ISlasher _slasher
) {
elAvsDirectory = _avsDirectory;
stakeRegistry = _stakeRegistry;
slasher = _slasher;
_disableInitializers();
}

// ============ Public Functions ============

/**
* @notice Updates the metadata URI for the AVS
* @param _metadataURI is the metadata URI for the AVS
* @dev only callable by the owner
*/
function updateAVSMetadataURI(
string memory _metadataURI
) public virtual onlyOwner {
elAvsDirectory.updateAVSMetadataURI(_metadataURI);
}

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS
* @param operator The address of the operator to register.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToAVS(
address operator,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) public virtual onlyStakeRegistry {
elAvsDirectory.registerOperatorToAVS(operator, operatorSignature);
emit OperatorRegisteredToAVS(operator);
}
Fixed Show fixed Hide fixed

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(
address operator
) public virtual onlyStakeRegistry {
elAvsDirectory.deregisterOperatorFromAVS(operator);
emit OperatorDeregisteredToAVS(operator);
}
Fixed Show fixed Hide fixed

function freezeOperator(address operator) public virtual onlyOwner {
slasher.freezeOperator(operator);
}

// ============ External Functions ============

/**
* @notice Returns the list of strategies that the AVS supports for restaking
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on uniqueness of each element in the returned array.
* The off-chain service should do that validation separately
*/
function getRestakeableStrategies()
external
view
returns (address[] memory)
{
return _getRestakeableStrategies();
}

/**
* @notice Returns the list of strategies that the operator has potentially restaked on the AVS
* @dev This function is intended to be called off-chain
* @dev Since ECDSAStakeRegistry only supports one quorum, each operator restakes into all the AVS strategies
* @dev No guarantee is made on uniqueness of each element in the returned array.
* The off-chain service should do that validation separately
*/
function getOperatorRestakedStrategies(
address /* operator */
) external view returns (address[] memory) {
return _getRestakeableStrategies();
}

/// @notice Returns the EigenLayer AVSDirectory contract.
function avsDirectory() external view override returns (address) {
return address(elAvsDirectory);
}

// ============ Internal Function ============

function _getRestakeableStrategies()
internal
view
returns (address[] memory)
{
uint256 strategyCount = stakeRegistry.quorum().strategies.length;

if (strategyCount == 0) {
return new address[](0);
}

address[] memory restakedStrategies = new address[](strategyCount);
for (uint256 i = 0; i < strategyCount; i++) {
restakedStrategies[i] = address(
stakeRegistry.quorum().strategies[i].strategy
);
}
return restakedStrategies;
}

// storage gap for upgradeability
// slither-disable-next-line shadowing-state
uint256[50] private __GAP;
}
176 changes: 176 additions & 0 deletions solidity/contracts/avs/HyperlaneServiceManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

import "forge-std/console.sol";

import {IAVSDirectory} from "@eigenlayer/interfaces/IAVSDirectory.sol";
import {ISlasher} from "@eigenlayer/interfaces/ISlasher.sol";
import {ECDSAStakeRegistry} from "@eigenlayer/middleware/unaudited/ECDSAStakeRegistry.sol";

import {Enrollment, EnrollmentStatus, EnumerableMapEnrollment} from "../libs/EnumerableMapEnrollment.sol";
import {IRemoteChallenger} from "../interfaces/avs/IRemoteChallenger.sol";
import {ECDSAServiceManagerBase} from "./ECDSAServiceManagerBase.sol";

contract HyperlaneServiceManager is ECDSAServiceManagerBase {
using EnumerableMapEnrollment for EnumerableMapEnrollment.AddressToEnrollmentMap;

event OperatorEnrolledToChallenger(
address operator,
IRemoteChallenger challenger
);
event OperatorQueuedUnenrollmentFromChallenger(
address operator,
IRemoteChallenger challenger,
uint256 unenrollmentStartBlock,
uint256 challengeDelayBlocks
);
event OperatorUnenrolledFromChallenger(
address operator,
IRemoteChallenger challenger,
uint256 unenrollmentEndBlock,
uint256 challengeDelayBlocks
);

mapping(address => EnumerableMapEnrollment.AddressToEnrollmentMap) enrolledChallengers;

modifier onlyEnrolledChallenger(address operator) {
(bool exists, ) = enrolledChallengers[operator].tryGet(msg.sender);
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
require(
exists,
"HyperlaneServiceManager: Operator not enrolled in challenger"
);
_;
}
Dismissed Show dismissed Hide dismissed

// ============ Constructor ============

constructor(
IAVSDirectory _avsDirectory,
ECDSAStakeRegistry _stakeRegistry,
ISlasher _slasher
) ECDSAServiceManagerBase(_avsDirectory, _stakeRegistry, _slasher) {}

// ============ Public Functions ============

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(
address operator
) public virtual override onlyStakeRegistry {
address[] memory challengers = getOperatorChallengers(operator);
for (uint256 i = 0; i < challengers.length; i++) {
IRemoteChallenger challenger = IRemoteChallenger(challengers[i]);
Enrollment memory enrollment = enrolledChallengers[operator].get(
challengers[i]
);
require(
enrollment.status != EnrollmentStatus.ENROLLED,
string(
abi.encodePacked(
"HyperlaneServiceManager: Operator still enrolled in challenger",
challengers[i]
)
)
);
enrolledChallengers[operator].remove(challengers[i]);
}
elAvsDirectory.deregisterOperatorFromAVS(operator);
}
Fixed Show fixed Hide fixed

// ============ External Functions ============

function enrollIntoChallengers(
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
IRemoteChallenger[] memory _challengers
) external {
for (uint256 i = 0; i < _challengers.length; i++) {
IRemoteChallenger challenger = _challengers[i];
enrolledChallengers[msg.sender].set(
address(challenger),
Enrollment(EnrollmentStatus.ENROLLED, 0)
);
emit OperatorEnrolledToChallenger(msg.sender, challenger);
}
}
Fixed Show fixed Hide fixed

function queueUnenrollmentFromChallengers(
IRemoteChallenger[] memory _challengers
) external {
for (uint256 i = 0; i < _challengers.length; i++) {
IRemoteChallenger challenger = _challengers[i];
(bool exists, Enrollment memory enrollment) = enrolledChallengers[
msg.sender
].tryGet(address(challenger));
console.log(
"queueUnenrollmentFromChallengers",
exists,
uint8(enrollment.status)
);
if (exists && enrollment.status == EnrollmentStatus.ENROLLED) {
enrolledChallengers[msg.sender].set(
address(challenger),
Enrollment(
EnrollmentStatus.PENDING_UNENROLLMENT,
uint248(block.number)
)
);
emit OperatorQueuedUnenrollmentFromChallenger(
msg.sender,
challenger,
block.number,
challenger.challengeDelayBlocks()
);
}
}
}
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed

function completeQueuedUnenrollmentFromChallengers(
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
IRemoteChallenger[] memory _challengers
) external {
for (uint256 i = 0; i < _challengers.length; i++) {
IRemoteChallenger challenger = _challengers[i];
(bool exists, Enrollment memory enrollment) = enrolledChallengers[
msg.sender
].tryGet(address(challenger));
if (
exists &&
enrollment.status == EnrollmentStatus.PENDING_UNENROLLMENT &&
block.number >=
enrollment.unenrollmentStartBlock +
challenger.challengeDelayBlocks()
) {
enrolledChallengers[msg.sender].remove(address(challenger));
emit OperatorUnenrolledFromChallenger(
msg.sender,
challenger,
block.number,
challenger.challengeDelayBlocks()
);
}
}
}
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed

function freezeOperator(
address operator
) public virtual override onlyEnrolledChallenger(operator) {
slasher.freezeOperator(operator);
}

function getEnrolledChallenger(
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
address _operator,
IRemoteChallenger _challenger
) external view returns (Enrollment memory enrollment) {
address[] memory keys = enrolledChallengers[_operator].keys();
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
for (uint256 i = 0; i < keys.length; i++) {
console.log("key", keys[i], address(_challenger));
}
return enrolledChallengers[_operator].get(address(_challenger));
}

function getOperatorChallengers(
address _operator
) public view returns (address[] memory) {
return enrolledChallengers[_operator].keys();
}
}
19 changes: 19 additions & 0 deletions solidity/contracts/interfaces/avs/IRemoteChallenger.sol
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/

interface IRemoteChallenger {
function challengeDelayBlocks() external view returns (uint256);
function handleChallenge(address operator) external;
}
Loading
Loading