diff --git a/contracts/Makefile b/contracts/Makefile index 6f3d7c0fd..05ddcbced 100644 --- a/contracts/Makefile +++ b/contracts/Makefile @@ -20,6 +20,10 @@ deploy-core: forge clean DEPLOY_TYPE="core" RPC_URL="http://localhost:8545" PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" CHAIN_ID="17864" ./entrypoint.sh +deploy-avs-with-mock-eigen: + forge clean + forge script scripts/validator-registry/avs/DeployAVSWithMockEigen.s.sol:DeployAVSWithMockEigen --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --via-ir --broadcast + run-stake-example: forge script scripts/validator-registry/ValidatorExampleScript.s.sol:StakeExample --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --via-ir --broadcast diff --git a/contracts/scripts/validator-registry/avs/DeployAVSWithMockEigen.s.sol b/contracts/scripts/validator-registry/avs/DeployAVSWithMockEigen.s.sol new file mode 100644 index 000000000..df084d2a4 --- /dev/null +++ b/contracts/scripts/validator-registry/avs/DeployAVSWithMockEigen.s.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BSL 1.1 +pragma solidity ^0.8.20; + +// solhint-disable no-console +// solhint-disable one-contract-per-file + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {MevCommitAVS} from "../../../contracts/validator-registry/avs/MevCommitAVS.sol"; +import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; +import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; +import {EigenPodManagerMock} from "../../../test/validator-registry/avs/EigenPodManagerMock.sol"; +import {AVSDirectoryMock} from "../../../test/validator-registry/avs/AVSDirectoryMock.sol"; + +contract DeployAVSWithMockEigen is Script { + function run() external { + require(block.chainid == 31337, "must deploy on anvil"); + vm.startBroadcast(); + + StrategyManagerMock strategyManagerMock = new StrategyManagerMock(); + DelegationManagerMock delegationManagerMock = new DelegationManagerMock(); + EigenPodManagerMock eigenPodManagerMock = new EigenPodManagerMock(); + AVSDirectoryMock avsDirectoryMock = new AVSDirectoryMock(); + + address[] memory restakeableStrategies = new address[](3); + restakeableStrategies[0] = address(0x1); + restakeableStrategies[1] = address(0x2); + restakeableStrategies[2] = address(0x3); + + address freezeOracle = address(0x5); + uint256 unfreezeFee = 1 ether; + address unfreezeReceiver = address(0x6); + uint256 unfreezePeriodBlocks = 100; + uint256 operatorDeregPeriodBlocks = 200; + uint256 validatorDeregPeriodBlocks = 300; + uint256 lstRestakerDeregPeriodBlocks = 400; + string memory metadataUrl = "https://raw.githubusercontent.com/primev/mev-commit/main/static/avs-metadata.json"; + + address proxy = Upgrades.deployUUPSProxy( + "MevCommitAVS.sol", + abi.encodeCall(MevCommitAVS.initialize, ( + msg.sender, + delegationManagerMock, + eigenPodManagerMock, + strategyManagerMock, + avsDirectoryMock, + restakeableStrategies, + freezeOracle, + unfreezeFee, + unfreezeReceiver, + unfreezePeriodBlocks, + operatorDeregPeriodBlocks, + validatorDeregPeriodBlocks, + lstRestakerDeregPeriodBlocks, + metadataUrl + )) + ); + MevCommitAVS mevCommitAVS = MevCommitAVS(payable(proxy)); + + console.log("StrategyManagerMock deployed at:", address(strategyManagerMock)); + console.log("DelegationManagerMock deployed at:", address(delegationManagerMock)); + console.log("EigenPodManagerMock deployed at:", address(eigenPodManagerMock)); + console.log("AVSDirectoryMock deployed at:", address(avsDirectoryMock)); + console.log("MevCommitAVS deployed at:", address(mevCommitAVS)); + + delegationManagerMock.setIsOperator(msg.sender, true); + + vm.stopBroadcast(); + } +} diff --git a/contracts/test/validator-registry/avs/AVSDirectoryMock.sol b/contracts/test/validator-registry/avs/AVSDirectoryMock.sol index 28967fef4..5eb9f85b6 100644 --- a/contracts/test/validator-registry/avs/AVSDirectoryMock.sol +++ b/contracts/test/validator-registry/avs/AVSDirectoryMock.sol @@ -4,10 +4,18 @@ pragma solidity 0.8.20; import "forge-std/Test.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // Eigenlayer core does not define their own mock for AVSDirectory.sol, hence we define our own. contract AVSDirectoryMock is IAVSDirectory, Test { mapping(address => bool) public isOperatorRegistered; + address public avs_; + + bytes32 public constant OPERATOR_AVS_REGISTRATION_TYPEHASH = + keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)"); + + bytes32 public constant DOMAIN_TYPEHASH = + keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); function registerOperator(address operator) external { isOperatorRegistered[operator] = true; @@ -17,16 +25,28 @@ contract AVSDirectoryMock is IAVSDirectory, Test { isOperatorRegistered[operator] = false; } - function isRegisteredOperator(address operator) external view returns (bool) { - return isOperatorRegistered[operator]; - } - function registerOperatorToAVS( address operator, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) external override { - require(operator != address(0), "Operator cannot be zero"); - require(operatorSignature.salt != bytes32(0), "Salt cannot be zero"); + require(operator != address(0), "Operator required"); + require(keccak256(operatorSignature.signature) != keccak256(bytes("")), "Signature required"); + require(operatorSignature.salt != bytes32(0), "Salt required"); + require(operatorSignature.expiry != 0, "Expiry required"); + + bytes32 operatorRegistrationDigestHash = calculateOperatorAVSRegistrationDigestHash({ + operator: operator, + avs: msg.sender, + salt: operatorSignature.salt, + expiry: operatorSignature.expiry + }); + + // solhint-disable-next-line reason-string + require( + ECDSA.recover(operatorRegistrationDigestHash, operatorSignature.signature) == operator, + "EIP1271SignatureUtils.checkSignature_EIP1271: signature not from signer" + ); + isOperatorRegistered[operator] = true; emit OperatorAVSRegistrationStatusUpdated(operator, msg.sender, OperatorAVSRegistrationStatus.REGISTERED); } @@ -40,6 +60,10 @@ contract AVSDirectoryMock is IAVSDirectory, Test { emit AVSMetadataURIUpdated(msg.sender, metadataURI); } + function isRegisteredOperator(address operator) external view returns (bool) { + return isOperatorRegistered[operator]; + } + function operatorSaltIsSpent(address operator, bytes32 salt) external pure override returns (bool) { require(operator != address(0), "Operator cannot be zero"); require(salt != bytes32(0), "Salt cannot be zero"); @@ -51,11 +75,17 @@ contract AVSDirectoryMock is IAVSDirectory, Test { address avs, bytes32 salt, uint256 expiry - ) external pure override returns (bytes32) { - return keccak256(abi.encodePacked(operator, avs, salt, expiry)); + ) public view returns (bytes32) { + bytes32 structHash = keccak256( + abi.encode(OPERATOR_AVS_REGISTRATION_TYPEHASH, operator, avs, salt, expiry) + ); + bytes32 digestHash = keccak256( + abi.encodePacked("\x19\x01", domainSeparator(), structHash) + ); + return digestHash; } - function OPERATOR_AVS_REGISTRATION_TYPEHASH() external pure override returns (bytes32) { - return keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)"); + function domainSeparator() public view returns (bytes32) { + return keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("EigenLayer")), block.chainid, address(this))); } } diff --git a/contracts/test/validator-registry/avs/MevCommitAVSTest.sol b/contracts/test/validator-registry/avs/MevCommitAVSTest.sol index 363df57cd..7e1db0fa7 100644 --- a/contracts/test/validator-registry/avs/MevCommitAVSTest.sol +++ b/contracts/test/validator-registry/avs/MevCommitAVSTest.sol @@ -31,6 +31,9 @@ contract MevCommitAVSTest is Test { uint256 public lstRestakerDeregPeriodBlocks; string public metadataUrl; + address public operator = address(0x18A8E44e0E225B10a4Af86CEC6e4c514BB95B342); + uint256 public operatorPrivateKey = uint256(0xe0ea92e36ee0c574bc092425926b3bfe817ec9471afbe90b577757ee16f60fd8); + event OperatorRegistered(address indexed operator); event OperatorDeregistrationRequested(address indexed operator); event OperatorDeregistered(address indexed operator); @@ -95,9 +98,16 @@ contract MevCommitAVSTest is Test { function testRegisterOperator() public { - address operator = address(0x888); + bytes32 digestHash = avsDirectoryMock.calculateOperatorAVSRegistrationDigestHash({ + operator: operator, + avs: address(mevCommitAVS), + salt: bytes32("salt"), + expiry: block.timestamp + 1 days + }); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operatorPrivateKey, digestHash); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = ISignatureUtils.SignatureWithSaltAndExpiry({ - signature: bytes("signature"), + signature: abi.encodePacked(r, s, v), salt: bytes32("salt"), expiry: block.timestamp + 1 days }); @@ -133,8 +143,6 @@ contract MevCommitAVSTest is Test { function testRequestOperatorDeregistration() public { vm.roll(108); - address operator = address(0x888); - vm.prank(owner); mevCommitAVS.pause(); vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); @@ -176,8 +184,6 @@ contract MevCommitAVSTest is Test { function testDeregisterOperator() public { vm.roll(11); - address operator = address(0x888); - vm.prank(owner); mevCommitAVS.pause(); vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); @@ -232,7 +238,6 @@ contract MevCommitAVSTest is Test { function testRegisterValidatorsByPodOwners() public { vm.roll(55); - address operator = address(0x888); address podOwner = address(0x420); ISignatureUtils.SignatureWithExpiry memory sig = ISignatureUtils.SignatureWithExpiry({ signature: bytes("signature"), @@ -332,7 +337,6 @@ contract MevCommitAVSTest is Test { function testRequestValidatorsDeregistration() public { - address operator = address(0x888); address podOwner = address(0x420); bytes[] memory valPubkeys = new bytes[](2); @@ -388,7 +392,6 @@ contract MevCommitAVSTest is Test { function testDeregisterValidator() public { - address operator = address(0x888); address podOwner = address(0x420); bytes[] memory valPubkeys = new bytes[](1); valPubkeys[0] = bytes("valPubkey1"); @@ -447,7 +450,6 @@ contract MevCommitAVSTest is Test { function testRegisterLSTRestaker() public { - address operator = address(0x888); address lstRestaker = address(0x34443); address otherAcct = address(0x777); bytes[] memory chosenVals = new bytes[](0); @@ -631,7 +633,6 @@ contract MevCommitAVSTest is Test { vm.roll(403); - address operator = address(0x888); bytes[] memory valPubkeys2 = new bytes[](1); valPubkeys2[0] = bytes("valPubkey1"); vm.prank(operator); @@ -699,7 +700,6 @@ contract MevCommitAVSTest is Test { assertTrue(mevCommitAVS.getValidatorRegInfo(valPubkeys[1]).freezeHeight.exists); address lstRestaker = address(0x34443); - address operator = address(0x888); ISignatureUtils.SignatureWithExpiry memory sig = ISignatureUtils.SignatureWithExpiry({ signature: bytes("signature"), expiry: 10 @@ -933,7 +933,6 @@ contract MevCommitAVSTest is Test { function testValidatorsWithReqDeregisteredOperatorsCannotRegister() public { testRegisterOperator(); - address operator = address(0x888); vm.prank(operator); mevCommitAVS.requestOperatorDeregistration(operator); @@ -1012,19 +1011,28 @@ contract MevCommitAVSTest is Test { assertTrue(mevCommitAVS.isValidatorOptedIn(valPubkeys[0])); assertTrue(mevCommitAVS.isValidatorOptedIn(valPubkeys[1])); - address operator = address(0x888); vm.prank(operator); mevCommitAVS.requestOperatorDeregistration(operator); assertFalse(mevCommitAVS.isValidatorOptedIn(valPubkeys[0])); assertFalse(mevCommitAVS.isValidatorOptedIn(valPubkeys[1])); - address newOperator = address(0x999); + address newOperator = address(0x0c94D2aE152F29Bf68A78dc9747BEa59B6f01418); + uint256 newOperatorPrivateKey = uint256(0x61437e7186d6d418e8c3221a88ce4c4aafba32347414d113ed31c425a99085a6); delegationManagerMock.setIsOperator(newOperator, true); + bytes32 digestHash = avsDirectoryMock.calculateOperatorAVSRegistrationDigestHash({ + operator: newOperator, + avs: address(mevCommitAVS), + salt: bytes32("salt"), + expiry: block.timestamp + 1 days + }); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(newOperatorPrivateKey, digestHash); + vm.prank(newOperator); ISignatureUtils.SignatureWithSaltAndExpiry memory newOperatorSigWithSalt = ISignatureUtils.SignatureWithSaltAndExpiry({ - signature: bytes("signature"), + signature: abi.encodePacked(r, s, v), salt: bytes32("salt"), expiry: block.timestamp + 1 days }); @@ -1046,7 +1054,6 @@ contract MevCommitAVSTest is Test { function testDeregisteredOperatorCanStillDeregisterValidators() public { testRegisterValidatorsByPodOwners(); - address operator = address(0x888); vm.prank(operator); mevCommitAVS.requestOperatorDeregistration(operator); assertTrue(mevCommitAVS.getOperatorRegInfo(operator).exists);