diff --git a/.github/bin/storage-report.sh b/.github/bin/storage-report.sh new file mode 100644 index 00000000..71fbd9a0 --- /dev/null +++ b/.github/bin/storage-report.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Default output directory +OUTPUT_DIR=${1:-docs/storage-report} + +# Function to print messages +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" +} + +# Function to print error messages +error() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >&2 +} + +log "Starting the storage report generation." + +# Create the output directory if it doesn't exist +if ! mkdir -p "$OUTPUT_DIR"; then + error "Failed to create output directory: $OUTPUT_DIR" + exit 1 +fi + +log "Output directory is set to: $OUTPUT_DIR" + +# Loop through Solidity files and generate storage report +# NOTE: Ignores `src/interfaces` & `src/libraries` since they "should" not contain storage logic. +for file in $(find src/ -name "*.sol" ! -path "*/interfaces/*" ! -path "*/libraries/*"); do + contract_name=$(basename "$file" .sol) + + # Check if the file exists and is readable + if [ ! -r "$file" ]; then + error "Cannot read file: $file" + continue + fi + + log "Processing contract: $contract_name" + + # Run forge inspect and capture errors + if ! forge inspect "$contract_name" storage --pretty > "$OUTPUT_DIR/$contract_name.md"; then + error "Failed to generate storage report for contract: $contract_name" + else + log "Storage report generated for contract: $contract_name" + fi +done \ No newline at end of file diff --git a/.github/workflows/storage-report.yml b/.github/workflows/storage-report.yml new file mode 100644 index 00000000..5d7f50d3 --- /dev/null +++ b/.github/workflows/storage-report.yml @@ -0,0 +1,48 @@ +name: Storage Layout + +on: + push: + branches: + - master + - mainnet + - testnet-goerli + - dev + pull_request: + +jobs: + check_storage: + name: CI + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: "Generate and prepare the storage reports for current branch" + run: | + bash .github/bin/storage-report.sh pr + + - name: Checkout dev + env: + TARGET: ${{ github.event.pull_request.base.sha }} + run: | + git fetch origin $TARGET + git checkout $TARGET + + - name: "Generate and prepare the storage reports for target branch" + run: | + bash .github/bin/storage-report.sh target + + - name: Compare outputs + run: | + if diff --unified pr target; then + echo "No differences found" + else + echo "::error::Differences found between PR and target branch storage layouts" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9ce44c3e..102bf394 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,9 +13,6 @@ on: - dev pull_request: -env: - FOUNDRY_PROFILE: ci - jobs: check: strategy: @@ -42,7 +39,7 @@ jobs: - name: Run tests shell: bash run: | - forge test --no-match-contract FFI + forge test --no-match-contract "FFI|ForkTest" env: RPC_MAINNET: ${{ secrets.RPC_MAINNET }} diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..db84aea5 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +storage-report: + bash ".github/bin/storage-report.sh" "docs/storage-report/" \ No newline at end of file diff --git a/README.md b/README.md index f33b6920..62b879a8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ EigenLayer is a set of smart contracts deployed on Ethereum that enable restakin ## Branching The main branches we use are: + * [`dev (default)`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/dev): The most up-to-date branch, containing the work-in-progress code for upcoming releases * [`testnet-holesky`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/testnet-holesky): Our current testnet deployment * [`mainnet`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/mainnet): Our current mainnet deployment @@ -24,6 +25,7 @@ The main branches we use are: ### Basics To get a basic understanding of EigenLayer, check out [You Could've Invented EigenLayer](https://www.blog.eigenlayer.xyz/ycie/). Note that some of the document's content describes features that do not exist yet (like the Slasher). To understand more about how restakers and operators interact with EigenLayer, check out these guides: + * [Restaking User Guide](https://docs.eigenlayer.xyz/restaking-guides/restaking-user-guide) * [Operator Guide](https://docs.eigenlayer.xyz/operator-guides/operator-introduction) @@ -32,10 +34,25 @@ Most of this content is intro-level and describes user interactions with the Eig ### Deep Dive For shadowy super-coders: + * The most up-to-date technical documentation can be found in [/docs](/docs). * To get an idea of how users interact with these contracts, check out the integration tests: [/test/integration](./test/integration) * To explore the EigenLayer core contracts, check out the core repo technical docs [here][core-docs-dev]. +### Note On Slashing + +The middleware contracts are available for testnet integration and experimentation. As with the rest of the testnet code, these contracts have not yet been fully audited. Internal security reviews are in progress, and we're preparing to engage external auditors. We're providing these now so AVSs can begin testing slashing conditions, Operator Sets, and related functionality, and so Operators can simulate slashing and test allocations. For more details, see [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md). + +For Operators: + +We provide scripts to deploy a mock service manager and allocate some stake to a mock Operator Set in [these scripts](https://github.com/Layr-Labs/eigenlayer-middleware/pull/335). +We've reduced various safety delays on testnet to better simulate operational flows. Additional information on these delays is in [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md). +For AVSs: + +We provide a migration path for existing M2 environments (LINK). While this requires understanding the provided script, the Developer Experience will improve before Mainnet launch. Note that this is a gradual migration process to new Operator Sets, not a direct upgrade of existing Operator Sets. +We also provide scripts to spin up new service managers from scratch. +In all cases, you’ll need to register existing Operators or spin up new Operators and allocate funds to them. These testing flows will improve as we refine the scripts and contracts. Stay tuned for updates, and feel free to reach out with any questions. + ## Building and Running Tests This repository uses Foundry. See the [Foundry docs](https://book.getfoundry.sh/) for more info on installation and usage. If you already have foundry, you can build this project and run tests with these commands: @@ -80,4 +97,3 @@ The current testnet deployment is on holesky, is from our M2 beta release. You c [`ServiceManagerRouter`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/testnet-holesky/src/ServiceManagerRouter.sol) | - | [`0x4463...5a37`](https://holesky.etherscan.io/address/0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37#code) | | [`ProxyAdmin`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/ProxyAdmin.sol) | - | [`0xB043...5c15`](https://holesky.etherscan.io/address/0xB043055dd967A382577c2f5261fA6428f2905c15) | | [`eigenda/EigenDAServiceManager`](https://github.com/Layr-Labs/eigenda/blob/a33b41561cc3fb4cd6d50a8738e4c5dca43ec0a5/contracts/src/core/EigenDAServiceManager.sol) | [`0xD4A7E1Bd8015057293f0D0A557088c286942e84b`](https://holesky.etherscan.io/address/0xD4A7E1Bd8015057293f0D0A557088c286942e84b) | [`0xa722...67f3`](https://holesky.etherscan.io/address/0xa7227485e6C693AC4566fe168C5E3647c5c267f3) | Proxy: [`TUP@4.7.1`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | - diff --git a/bin/storage-report.sh b/bin/storage-report.sh new file mode 100644 index 00000000..71fbd9a0 --- /dev/null +++ b/bin/storage-report.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Default output directory +OUTPUT_DIR=${1:-docs/storage-report} + +# Function to print messages +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" +} + +# Function to print error messages +error() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >&2 +} + +log "Starting the storage report generation." + +# Create the output directory if it doesn't exist +if ! mkdir -p "$OUTPUT_DIR"; then + error "Failed to create output directory: $OUTPUT_DIR" + exit 1 +fi + +log "Output directory is set to: $OUTPUT_DIR" + +# Loop through Solidity files and generate storage report +# NOTE: Ignores `src/interfaces` & `src/libraries` since they "should" not contain storage logic. +for file in $(find src/ -name "*.sol" ! -path "*/interfaces/*" ! -path "*/libraries/*"); do + contract_name=$(basename "$file" .sol) + + # Check if the file exists and is readable + if [ ! -r "$file" ]; then + error "Cannot read file: $file" + continue + fi + + log "Processing contract: $contract_name" + + # Run forge inspect and capture errors + if ! forge inspect "$contract_name" storage --pretty > "$OUTPUT_DIR/$contract_name.md"; then + error "Failed to generate storage report for contract: $contract_name" + else + log "Storage report generated for contract: $contract_name" + fi +done \ No newline at end of file diff --git a/docs/storage-report/AVSRegistrar.md b/docs/storage-report/AVSRegistrar.md new file mode 100644 index 00000000..1ec5dc07 --- /dev/null +++ b/docs/storage-report/AVSRegistrar.md @@ -0,0 +1,6 @@ + +╭------+------+------+--------+-------+----------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================+ +╰------+------+------+--------+-------+----------╯ + diff --git a/docs/storage-report/BLSApkRegistry.md b/docs/storage-report/BLSApkRegistry.md new file mode 100644 index 00000000..69239c7c --- /dev/null +++ b/docs/storage-report/BLSApkRegistry.md @@ -0,0 +1,21 @@ + +╭----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=============================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| operatorToPubkeyHash | mapping(address => bytes32) | 1 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| pubkeyHashToOperator | mapping(bytes32 => address) | 2 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| operatorToPubkey | mapping(address => struct BN254.G1Point) | 3 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| apkHistory | mapping(uint8 => struct IBLSApkRegistry.ApkUpdate[]) | 4 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| currentApk | mapping(uint8 => struct BN254.G1Point) | 5 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| __GAP | uint256[45] | 6 | 0 | 1440 | src/BLSApkRegistry.sol:BLSApkRegistry | +╰----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------╯ + diff --git a/docs/storage-report/BLSApkRegistryStorage.md b/docs/storage-report/BLSApkRegistryStorage.md new file mode 100644 index 00000000..3b5b9e61 --- /dev/null +++ b/docs/storage-report/BLSApkRegistryStorage.md @@ -0,0 +1,21 @@ + +╭----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++===========================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| operatorToPubkeyHash | mapping(address => bytes32) | 1 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| pubkeyHashToOperator | mapping(bytes32 => address) | 2 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| operatorToPubkey | mapping(address => struct BN254.G1Point) | 3 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| apkHistory | mapping(uint8 => struct IBLSApkRegistry.ApkUpdate[]) | 4 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| currentApk | mapping(uint8 => struct BN254.G1Point) | 5 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| __GAP | uint256[45] | 6 | 0 | 1440 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +╰----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------╯ + diff --git a/docs/storage-report/BLSSignatureChecker.md b/docs/storage-report/BLSSignatureChecker.md new file mode 100644 index 00000000..19b3473b --- /dev/null +++ b/docs/storage-report/BLSSignatureChecker.md @@ -0,0 +1,9 @@ + +╭----------------------+-------------+------+--------+-------+-------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==============================================================================================================+ +| staleStakesForbidden | bool | 0 | 0 | 1 | src/BLSSignatureChecker.sol:BLSSignatureChecker | +|----------------------+-------------+------+--------+-------+-------------------------------------------------| +| __GAP | uint256[49] | 1 | 0 | 1568 | src/BLSSignatureChecker.sol:BLSSignatureChecker | +╰----------------------+-------------+------+--------+-------+-------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAServiceManagerBase.md b/docs/storage-report/ECDSAServiceManagerBase.md new file mode 100644 index 00000000..2251f673 --- /dev/null +++ b/docs/storage-report/ECDSAServiceManagerBase.md @@ -0,0 +1,19 @@ + +╭------------------+-------------+------+--------+-------+-------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++============================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| rewardsInitiator | address | 101 | 0 | 20 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| __GAP | uint256[49] | 102 | 0 | 1568 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +╰------------------+-------------+------+--------+-------+-------------------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistry.md b/docs/storage-report/ECDSAStakeRegistry.md new file mode 100644 index 00000000..df768c27 --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistry.md @@ -0,0 +1,37 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==========================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _totalOperators | uint256 | 101 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _quorum | struct Quorum | 102 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _minimumWeight | uint256 | 103 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _serviceManager | address | 104 | 0 | 20 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _stakeExpiry | uint256 | 105 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 106 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 107 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 108 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 109 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 110 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| __gap | uint256[40] | 111 | 0 | 1280 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistryEqualWeight.md b/docs/storage-report/ECDSAStakeRegistryEqualWeight.md new file mode 100644 index 00000000..e1e1c166 --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistryEqualWeight.md @@ -0,0 +1,39 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=========================================================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _totalOperators | uint256 | 101 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _quorum | struct Quorum | 102 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _minimumWeight | uint256 | 103 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _serviceManager | address | 104 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _stakeExpiry | uint256 | 105 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 106 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 107 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 108 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 109 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 110 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| __gap | uint256[40] | 111 | 0 | 1280 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| allowlistedOperators | mapping(address => bool) | 151 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistryPermissioned.md b/docs/storage-report/ECDSAStakeRegistryPermissioned.md new file mode 100644 index 00000000..bdc5b32e --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistryPermissioned.md @@ -0,0 +1,39 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++===========================================================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _totalOperators | uint256 | 101 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _quorum | struct Quorum | 102 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _minimumWeight | uint256 | 103 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _serviceManager | address | 104 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _stakeExpiry | uint256 | 105 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 106 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 107 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 108 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 109 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 110 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| __gap | uint256[40] | 111 | 0 | 1280 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| allowlistedOperators | mapping(address => bool) | 151 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistryStorage.md b/docs/storage-report/ECDSAStakeRegistryStorage.md new file mode 100644 index 00000000..69ef3d05 --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistryStorage.md @@ -0,0 +1,27 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++========================================================================================================================================================================================+ +| _totalOperators | uint256 | 0 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _quorum | struct Quorum | 1 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _minimumWeight | uint256 | 2 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _serviceManager | address | 3 | 0 | 20 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _stakeExpiry | uint256 | 4 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 5 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 6 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 7 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 8 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 9 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| __gap | uint256[40] | 10 | 0 | 1280 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------╯ + diff --git a/docs/storage-report/EjectionManager.md b/docs/storage-report/EjectionManager.md new file mode 100644 index 00000000..55e2a2df --- /dev/null +++ b/docs/storage-report/EjectionManager.md @@ -0,0 +1,21 @@ + +╭-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==========================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| _owner | address | 51 | 0 | 20 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| isEjector | mapping(address => bool) | 101 | 0 | 32 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| stakeEjectedForQuorum | mapping(uint8 => struct IEjectionManager.StakeEjection[]) | 102 | 0 | 32 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| quorumEjectionParams | mapping(uint8 => struct IEjectionManager.QuorumEjectionParams) | 103 | 0 | 32 | src/EjectionManager.sol:EjectionManager | +╰-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------╯ + diff --git a/docs/storage-report/IndexRegistry.md b/docs/storage-report/IndexRegistry.md new file mode 100644 index 00000000..3415bdcd --- /dev/null +++ b/docs/storage-report/IndexRegistry.md @@ -0,0 +1,17 @@ + +╭-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++===================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| currentOperatorIndex | mapping(uint8 => mapping(bytes32 => uint32)) | 1 | 0 | 32 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _operatorIndexHistory | mapping(uint8 => mapping(uint32 => struct IIndexRegistry.OperatorUpdate[])) | 2 | 0 | 32 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _operatorCountHistory | mapping(uint8 => struct IIndexRegistry.QuorumUpdate[]) | 3 | 0 | 32 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| __GAP | uint256[47] | 4 | 0 | 1504 | src/IndexRegistry.sol:IndexRegistry | +╰-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------╯ + diff --git a/docs/storage-report/IndexRegistryStorage.md b/docs/storage-report/IndexRegistryStorage.md new file mode 100644 index 00000000..9581f514 --- /dev/null +++ b/docs/storage-report/IndexRegistryStorage.md @@ -0,0 +1,17 @@ + +╭-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=================================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| currentOperatorIndex | mapping(uint8 => mapping(bytes32 => uint32)) | 1 | 0 | 32 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _operatorIndexHistory | mapping(uint8 => mapping(uint32 => struct IIndexRegistry.OperatorUpdate[])) | 2 | 0 | 32 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _operatorCountHistory | mapping(uint8 => struct IIndexRegistry.QuorumUpdate[]) | 3 | 0 | 32 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| __GAP | uint256[47] | 4 | 0 | 1504 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +╰-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╯ + diff --git a/docs/storage-report/InstantSlasher.md b/docs/storage-report/InstantSlasher.md new file mode 100644 index 00000000..1eafc2b8 --- /dev/null +++ b/docs/storage-report/InstantSlasher.md @@ -0,0 +1,17 @@ + +╭----------------+-------------+------+--------+-------+------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=======================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| serviceManager | address | 0 | 2 | 20 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| slasher | address | 1 | 0 | 20 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| nextRequestId | uint256 | 2 | 0 | 32 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| __gap | uint256[47] | 3 | 0 | 1504 | src/slashers/InstantSlasher.sol:InstantSlasher | +╰----------------+-------------+------+--------+-------+------------------------------------------------╯ + diff --git a/docs/storage-report/OperatorStateRetriever.md b/docs/storage-report/OperatorStateRetriever.md new file mode 100644 index 00000000..1ec5dc07 --- /dev/null +++ b/docs/storage-report/OperatorStateRetriever.md @@ -0,0 +1,6 @@ + +╭------+------+------+--------+-------+----------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================+ +╰------+------+------+--------+-------+----------╯ + diff --git a/docs/storage-report/RegistryCoordinator.md b/docs/storage-report/RegistryCoordinator.md new file mode 100644 index 00000000..1adff5f4 --- /dev/null +++ b/docs/storage-report/RegistryCoordinator.md @@ -0,0 +1,49 @@ + +╭-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==============================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __deprecated_pauserRegistry | contract IPauserRegistry | 0 | 2 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _paused | uint256 | 1 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __gap | uint256[48] | 2 | 0 | 1536 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __gap | uint256[50] | 50 | 0 | 1600 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _owner | address | 100 | 0 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __gap | uint256[49] | 101 | 0 | 1568 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| quorumCount | uint8 | 150 | 0 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _quorumParams | mapping(uint8 => struct IRegistryCoordinator.OperatorSetParam) | 151 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _operatorBitmapHistory | mapping(bytes32 => struct IRegistryCoordinator.QuorumBitmapUpdate[]) | 152 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _operatorInfo | mapping(address => struct IRegistryCoordinator.OperatorInfo) | 153 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| isChurnApproverSaltUsed | mapping(bytes32 => bool) | 154 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| quorumUpdateBlockNumber | mapping(uint8 => uint256) | 155 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| registries | address[] | 156 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| churnApprover | address | 157 | 0 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| ejector | address | 158 | 0 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| lastEjectionTimestamp | mapping(address => uint256) | 159 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| ejectionCooldown | uint256 | 160 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| isOperatorSetAVS | bool | 161 | 0 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| isM2Quorum | mapping(uint8 => bool) | 162 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __GAP | uint256[37] | 163 | 0 | 1184 | src/RegistryCoordinator.sol:RegistryCoordinator | +╰-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------╯ + diff --git a/docs/storage-report/RegistryCoordinatorStorage.md b/docs/storage-report/RegistryCoordinatorStorage.md new file mode 100644 index 00000000..1e9fc25d --- /dev/null +++ b/docs/storage-report/RegistryCoordinatorStorage.md @@ -0,0 +1,33 @@ + +╭-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++========================================================================================================================================================================================+ +| quorumCount | uint8 | 0 | 0 | 1 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _quorumParams | mapping(uint8 => struct IRegistryCoordinator.OperatorSetParam) | 1 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _operatorBitmapHistory | mapping(bytes32 => struct IRegistryCoordinator.QuorumBitmapUpdate[]) | 2 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _operatorInfo | mapping(address => struct IRegistryCoordinator.OperatorInfo) | 3 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isChurnApproverSaltUsed | mapping(bytes32 => bool) | 4 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| quorumUpdateBlockNumber | mapping(uint8 => uint256) | 5 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| registries | address[] | 6 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| churnApprover | address | 7 | 0 | 20 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| ejector | address | 8 | 0 | 20 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| lastEjectionTimestamp | mapping(address => uint256) | 9 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| ejectionCooldown | uint256 | 10 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isOperatorSetAVS | bool | 11 | 0 | 1 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isM2Quorum | mapping(uint8 => bool) | 12 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| __GAP | uint256[37] | 13 | 0 | 1184 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +╰-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerBase.md b/docs/storage-report/ServiceManagerBase.md new file mode 100644 index 00000000..61ef533a --- /dev/null +++ b/docs/storage-report/ServiceManagerBase.md @@ -0,0 +1,27 @@ + +╭--------------------------+-------------+------+--------+-------+-----------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| rewardsInitiator | address | 101 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| slasher | address | 102 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| proposedSlasher | address | 103 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| slasherProposalTimestamp | uint256 | 104 | 0 | 32 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| migrationFinalized | bool | 105 | 0 | 1 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| __GAP | uint256[45] | 106 | 0 | 1440 | src/ServiceManagerBase.sol:ServiceManagerBase | +╰--------------------------+-------------+------+--------+-------+-----------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerBaseStorage.md b/docs/storage-report/ServiceManagerBaseStorage.md new file mode 100644 index 00000000..baa458fa --- /dev/null +++ b/docs/storage-report/ServiceManagerBaseStorage.md @@ -0,0 +1,27 @@ + +╭--------------------------+-------------+------+--------+-------+-------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==============================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| rewardsInitiator | address | 101 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| slasher | address | 102 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| proposedSlasher | address | 103 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| slasherProposalTimestamp | uint256 | 104 | 0 | 32 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| migrationFinalized | bool | 105 | 0 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __GAP | uint256[45] | 106 | 0 | 1440 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +╰--------------------------+-------------+------+--------+-------+-------------------------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerRouter.md b/docs/storage-report/ServiceManagerRouter.md new file mode 100644 index 00000000..1ec5dc07 --- /dev/null +++ b/docs/storage-report/ServiceManagerRouter.md @@ -0,0 +1,6 @@ + +╭------+------+------+--------+-------+----------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================+ +╰------+------+------+--------+-------+----------╯ + diff --git a/docs/storage-report/SlasherBase.md b/docs/storage-report/SlasherBase.md new file mode 100644 index 00000000..2e210170 --- /dev/null +++ b/docs/storage-report/SlasherBase.md @@ -0,0 +1,17 @@ + +╭----------------+-------------+------+--------+-------+-----------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++======================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| serviceManager | address | 0 | 2 | 20 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| slasher | address | 1 | 0 | 20 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| nextRequestId | uint256 | 2 | 0 | 32 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| __gap | uint256[47] | 3 | 0 | 1504 | src/slashers/base/SlasherBase.sol:SlasherBase | +╰----------------+-------------+------+--------+-------+-----------------------------------------------╯ + diff --git a/docs/storage-report/SlasherStorage.md b/docs/storage-report/SlasherStorage.md new file mode 100644 index 00000000..6a5ec3aa --- /dev/null +++ b/docs/storage-report/SlasherStorage.md @@ -0,0 +1,13 @@ + +╭----------------+-------------+------+--------+-------+-----------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++============================================================================================================+ +| serviceManager | address | 0 | 0 | 20 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +|----------------+-------------+------+--------+-------+-----------------------------------------------------| +| slasher | address | 1 | 0 | 20 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +|----------------+-------------+------+--------+-------+-----------------------------------------------------| +| nextRequestId | uint256 | 2 | 0 | 32 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +|----------------+-------------+------+--------+-------+-----------------------------------------------------| +| __gap | uint256[47] | 3 | 0 | 1504 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +╰----------------+-------------+------+--------+-------+-----------------------------------------------------╯ + diff --git a/docs/storage-report/StakeRegistry.md b/docs/storage-report/StakeRegistry.md new file mode 100644 index 00000000..820aaba6 --- /dev/null +++ b/docs/storage-report/StakeRegistry.md @@ -0,0 +1,21 @@ + +╭----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++============================================================================================================================================================================+ +| minimumStakeForQuorum | mapping(uint8 => uint96) | 0 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _totalStakeHistory | mapping(uint8 => struct IStakeRegistry.StakeUpdate[]) | 1 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| operatorStakeHistory | mapping(bytes32 => mapping(uint8 => struct IStakeRegistry.StakeUpdate[])) | 2 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| strategyParams | mapping(uint8 => struct IStakeRegistry.StrategyParams[]) | 3 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| strategiesPerQuorum | mapping(uint8 => contract IStrategy[]) | 4 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| stakeTypePerQuorum | mapping(uint8 => enum StakeType) | 5 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| slashableStakeLookAheadPerQuorum | mapping(uint8 => uint32) | 6 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| __GAP | uint256[43] | 7 | 0 | 1376 | src/StakeRegistry.sol:StakeRegistry | +╰----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------╯ + diff --git a/docs/storage-report/StakeRegistryStorage.md b/docs/storage-report/StakeRegistryStorage.md new file mode 100644 index 00000000..4a545513 --- /dev/null +++ b/docs/storage-report/StakeRegistryStorage.md @@ -0,0 +1,21 @@ + +╭----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==========================================================================================================================================================================================+ +| minimumStakeForQuorum | mapping(uint8 => uint96) | 0 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _totalStakeHistory | mapping(uint8 => struct IStakeRegistry.StakeUpdate[]) | 1 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| operatorStakeHistory | mapping(bytes32 => mapping(uint8 => struct IStakeRegistry.StakeUpdate[])) | 2 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| strategyParams | mapping(uint8 => struct IStakeRegistry.StrategyParams[]) | 3 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| strategiesPerQuorum | mapping(uint8 => contract IStrategy[]) | 4 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| stakeTypePerQuorum | mapping(uint8 => enum StakeType) | 5 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| slashableStakeLookAheadPerQuorum | mapping(uint8 => uint32) | 6 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| __GAP | uint256[43] | 7 | 0 | 1376 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +╰----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╯ + diff --git a/docs/storage-report/VetoableSlasher.md b/docs/storage-report/VetoableSlasher.md new file mode 100644 index 00000000..e69de29b diff --git a/foundry.toml b/foundry.toml index 4e13d2e8..4023fa98 100644 --- a/foundry.toml +++ b/foundry.toml @@ -10,12 +10,22 @@ no-match-contract = "FFI" # Enables or disables the optimizer optimizer = true -# The number of optimizer runs +# Sets the number of optimizer runs optimizer_runs = 200 # Whether or not to use the Yul intermediate representation compilation pipeline via_ir = false # Override the Solidity version (this overrides `auto_detect_solc`) -solc_version = '0.8.12' +solc_version = '0.8.27' + +[rpc_endpoints] +mainnet = "${MAINNET_RPC_URL}" +holesky = "${HOLESKY_RPC_URL}" +sepolia = "${SEPOLIA_RPC_URL}" +anvil = "${ANVIL_RPC_URL}" + +[etherscan] +mainnet = { key = "${ETHERSCAN_API_KEY}" } +holesky = { key = "${ETHERSCAN_API_KEY}" } [fmt] bracket_spacing = false diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 6c30b8ea..22de8094 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 6c30b8ea1b8338709c58008bc2dc8a248d303db7 +Subproject commit 22de809403924707ccce6998e62b868bfae0fc58 diff --git a/lib/forge-std b/lib/forge-std index bb4ceea9..1eea5bae 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 diff --git a/script/DeployMiddleware.s.sol b/script/DeployMiddleware.s.sol new file mode 100644 index 00000000..14d6a03e --- /dev/null +++ b/script/DeployMiddleware.s.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Script} from "forge-std/Script.sol"; +import {console2 as console} from "forge-std/Test.sol"; +import {UpgradeableProxyLib} from "./utils/UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./utils/CoreDeploymentLib.sol"; +import {MiddlewareDeploymentLib} from "./utils/MiddlewareDeploymentLib.sol"; + +contract DeployMiddleware is Script { + CoreDeploymentLib.DeploymentData internal core; + MiddlewareDeploymentLib.ConfigData internal config; + MiddlewareDeploymentLib.DeploymentData internal middlewareDeployment; + address internal deployer; + + function setUp() public { + /// TODO: Right now we're only supporting pre-prod + deployer = vm.rememberKey(vm.envUint("HOLESKY_PRIVATE_KEY")); + vm.label(deployer, "Deployer"); + + // Read core deployment data from json + core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", block.chainid, "preprod"); + + config.admin = deployer; + config.numQuorums = 1; + + uint256[] memory operatorParams = new uint256[](6); + operatorParams[0] = 10000; // maxOperatorCount for quorum 0 + operatorParams[1] = 2000; // kickBIPsOfOperatorStake for quorum 0 (20%) + operatorParams[2] = 500; // kickBIPsOfTotalStake for quorum 0 (5%) + operatorParams[3] = 10000; // maxOperatorCount for quorum 1 + operatorParams[4] = 2000; // kickBIPsOfOperatorStake for quorum 1 (20%) + operatorParams[5] = 500; // kickBIPsOfTotalStake for quorum 1 (5%) + config.operatorParams = operatorParams; + } + + function run() external { + vm.startBroadcast(deployer); + + address proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + middlewareDeployment = MiddlewareDeploymentLib.deployContracts(proxyAdmin, core, config); + + labelContracts(core, middlewareDeployment); + + MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, config, core); + + logDeploymentDetails(middlewareDeployment); + + vm.stopBroadcast(); + } + + function logDeploymentDetails(MiddlewareDeploymentLib.DeploymentData memory result) internal pure { + console.log("Deployment completed"); + console.log("ServiceManager:", result.serviceManager); + console.log("RegistryCoordinator:", result.registryCoordinator); + console.log("BLSApkRegistry:", result.blsapkRegistry); + console.log("IndexRegistry:", result.indexRegistry); + console.log("StakeRegistry:", result.stakeRegistry); + console.log("OperatorStateRetriever:", result.operatorStateRetriever); + console.log("Token:", result.token); + console.log("Strategy:", result.strategy); + } + + function labelContracts(CoreDeploymentLib.DeploymentData memory coreData, MiddlewareDeploymentLib.DeploymentData memory middlewareData) internal { + // Label core contracts + vm.label(coreData.delegationManager, "DelegationManager"); + vm.label(coreData.avsDirectory, "AVSDirectory"); + vm.label(coreData.strategyManager, "StrategyManager"); + vm.label(coreData.eigenPodManager, "EigenPodManager"); + vm.label(coreData.rewardsCoordinator, "RewardsCoordinator"); + vm.label(coreData.eigenPodBeacon, "EigenPodBeacon"); + vm.label(coreData.pauserRegistry, "PauserRegistry"); + vm.label(coreData.strategyFactory, "StrategyFactory"); + vm.label(coreData.strategyBeacon, "StrategyBeacon"); + + // Label middleware contracts + vm.label(middlewareData.registryCoordinator, "RegistryCoordinator"); + vm.label(middlewareData.serviceManager, "ServiceManager"); + vm.label(middlewareData.operatorStateRetriever, "OperatorStateRetriever"); + vm.label(middlewareData.blsapkRegistry, "BLSApkRegistry"); + vm.label(middlewareData.indexRegistry, "IndexRegistry"); + vm.label(middlewareData.stakeRegistry, "StakeRegistry"); + vm.label(middlewareData.strategy, "Strategy"); + vm.label(middlewareData.token, "Token"); + vm.label(middlewareData.pauserRegistry, "PauserRegistry"); + } +} diff --git a/script/MiddlewareUpgrade.s.sol b/script/MiddlewareUpgrade.s.sol new file mode 100644 index 00000000..276be73f --- /dev/null +++ b/script/MiddlewareUpgrade.s.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Script} from "forge-std/Script.sol"; +import {console2 as console} from "forge-std/Test.sol"; +import {UpgradeableProxyLib} from "./utils/UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./utils/CoreDeploymentLib.sol"; +import {MiddlewareDeploymentLib} from "./utils/MiddlewareDeploymentLib.sol"; +import {stdJson} from "forge-std/StdJson.sol"; + +contract MiddlewareUpgrade is Script { + using stdJson for string; + + CoreDeploymentLib.DeploymentData internal core; + MiddlewareDeploymentLib.DeploymentData internal middlewareDeployment; + address internal deployer; + + function setUp() public { + /// TODO: Right now we're only supporting pre-prod + deployer = vm.rememberKey(vm.envUint("HOLESKY_PRIVATE_KEY")); + vm.label(deployer, "Deployer"); + + // Read core deployment data from json + core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", block.chainid, "preprod"); + + // Read existing middleware deployment from json + middlewareDeployment = MiddlewareDeploymentLib.readDeploymentJson("./script/deployments", block.chainid, "preprod"); + + } + + function run() external { + vm.startBroadcast(deployer); + + // Upgrade contracts + MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, core); + + // Write updated deployment info to json + string memory deploymentJson = generateDeploymentJson(); + vm.writeFile(string(abi.encodePacked("./script/deployments/", vm.toString(uint256(block.chainid)), "-preprod.json")), deploymentJson); + + logDeploymentDetails(middlewareDeployment); + + vm.stopBroadcast(); + } + + function generateDeploymentJson() internal view returns (string memory) { + return string.concat( + "{", + string.concat( + '"serviceManager":"', vm.toString(middlewareDeployment.serviceManager), '",', + '"registryCoordinator":"', vm.toString(middlewareDeployment.registryCoordinator), '",', + '"blsapkRegistry":"', vm.toString(middlewareDeployment.blsapkRegistry), '",', + '"indexRegistry":"', vm.toString(middlewareDeployment.indexRegistry), '",', + '"stakeRegistry":"', vm.toString(middlewareDeployment.stakeRegistry), '",', + '"operatorStateRetriever":"', vm.toString(middlewareDeployment.operatorStateRetriever), '",', + '"token":"', vm.toString(middlewareDeployment.token), '",', + '"strategy":"', vm.toString(middlewareDeployment.strategy), '"' + ), + "}" + ); + } + + function logDeploymentDetails(MiddlewareDeploymentLib.DeploymentData memory result) internal pure { + console.log("Upgrade completed"); + console.log("ServiceManager:", result.serviceManager); + console.log("RegistryCoordinator:", result.registryCoordinator); + console.log("BLSApkRegistry:", result.blsapkRegistry); + console.log("IndexRegistry:", result.indexRegistry); + console.log("StakeRegistry:", result.stakeRegistry); + console.log("OperatorStateRetriever:", result.operatorStateRetriever); + console.log("Token:", result.token); + console.log("Strategy:", result.strategy); + } +} diff --git a/script/ServiceManagerRouterDeploy.s.sol b/script/ServiceManagerRouterDeploy.s.sol index 6a61a796..d87315b3 100644 --- a/script/ServiceManagerRouterDeploy.s.sol +++ b/script/ServiceManagerRouterDeploy.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../src/ServiceManagerRouter.sol"; import "forge-std/Script.sol"; diff --git a/script/config/17000-preprod.json b/script/config/17000-preprod.json new file mode 100644 index 00000000..613248fa --- /dev/null +++ b/script/config/17000-preprod.json @@ -0,0 +1 @@ +{"ZEUS_ENV":"preprod","ZEUS_ENV_COMMIT":"26dcafff7bd304b84f81ba36fe70ef44d414a752","ZEUS_TEST":"false","ZEUS_ENV_VERSION":"1.0.0","ZEUS_VERSION":"0.3.0","ZEUS_ENV_communityMultisig":"0x42789cCdE6486b81c7D41Fa3759A8BB74D7909DB","ZEUS_ENV_executorMultisig":"0x84E5e0D6f6c3153057bd5d661Be1ff1766cEac08","ZEUS_ENV_protocolCouncilMultisig":"0x314A6B61B5CBFA79dc7b2c0416e039822E886b54","ZEUS_ENV_operationsMultisig":"0x6d609cD2812bDA02a75dcABa7DaafE4B20Ff5608","ZEUS_ENV_pauserMultisig":"0x546D3b66B27dCb94777d7eFC3825b10675FE5D95","ZEUS_ENV_pauserRegistry":"0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1","ZEUS_ENV_proxyAdmin":"0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B","ZEUS_ENV_timelockController":"0x7c66A1e862E11C4887270aBd649157ACe837A2D0","ZEUS_ENV_timelockController_BEIGEN":"0x68fa0fB9eDe34745C41d85E2766ee32eBc6cEF0e","ZEUS_ENV_beigenExecutorMultisig":"0xFbb2c01A19A85c4C648186a97d879EEA25cE1397","ZEUS_ENV_foundationMultisig":"0x9c36f012585c8bb4247eEe18C01Cdfc5f2e90336","ZEUS_ENV_ethPOS":"0x4242424242424242424242424242424242424242","ZEUS_ENV_MultiSendCallOnly":"0x40A2aCCbd92BCA938b02010E17A5b8929b49130D","ZEUS_ENV_MIN_WITHDRAWAL_DELAY":50,"ZEUS_ENV_ALLOCATION_CONFIGURATION_DELAY":75,"ZEUS_ENV_EIGENPOD_GENESIS_TIME":1695902400,"ZEUS_ENV_REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS":86400,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_REWARDS_DURATION":6048000,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH":7776000,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_FUTURE_LENGTH":2592000,"ZEUS_ENV_REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP":1710979200,"ZEUS_ENV_REWARDS_COORDINATOR_INIT_PAUSED_STATUS":0,"ZEUS_ENV_REWARDS_COORDINATOR_UPDATER":"0x18a0f92Ad9645385E8A8f3db7d0f6CF7aBBb0aD4","ZEUS_ENV_REWARDS_COORDINATOR_ACTIVATION_DELAY":120,"ZEUS_ENV_REWARDS_COORDINATOR_DEFAULT_OPERATOR_SPLIT_BIPS":1000,"ZEUS_ENV_REWARDS_COORDINATOR_PAUSE_STATUS":2,"ZEUS_DEPLOYED_PauserRegistry_Impl":"0x50712285cE831a6B9a11214A430f28999A5b4DAe","ZEUS_DEPLOYED_AVSDirectory_Proxy":"0x141d6995556135D4997b2ff72EB443Be300353bC","ZEUS_DEPLOYED_AVSDirectory_Impl":"0xD5597C5c574BbD4Ce76C3aaF2900525De97aD711","ZEUS_DEPLOYED_EigenPod_Beacon":"0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC","ZEUS_DEPLOYED_EigenPod_Impl":"0xf53F78382a26b62A3aC3a3837a8dED2044a2103D","ZEUS_DEPLOYED_EigenPodManager_Proxy":"0xB8d8952f572e67B11e43bC21250967772fa883Ff","ZEUS_DEPLOYED_EigenPodManager_Impl":"0xe55A9EA92b1134afF44fB81e667FD5f74e6a47e5","ZEUS_DEPLOYED_DelegationManager_Proxy":"0x75dfE5B44C2E530568001400D3f704bC8AE350CC","ZEUS_DEPLOYED_DelegationManager_Impl":"0xa61d360551d05715046491eEc8a8Cb90f6867545","ZEUS_DEPLOYED_RewardsCoordinator_Proxy":"0xb22Ef643e1E067c994019A4C19e403253C05c2B0","ZEUS_DEPLOYED_RewardsCoordinator_Impl":"0x480e9045F62F20b8B8342164011305f223D48B2b","ZEUS_DEPLOYED_EigenStrategy_Proxy":"0x4e0125f8a928Eb1b9dB4BeDd3756BA3c200563C2","ZEUS_DEPLOYED_EigenStrategy_Impl":"0xdB01e5178E4745346B230fdF2d6Da2F483a71EA4","ZEUS_DEPLOYED_Eigen_Proxy":"0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926","ZEUS_DEPLOYED_Eigen_Impl":"0x95a7431400F362F3647a69535C5666cA0133CAA0","ZEUS_DEPLOYED_BackingEigen_Proxy":"0xA72942289a043874249E60469F68f08B8c6ECCe8","ZEUS_DEPLOYED_BackingEigen_Impl":"0xd5FdabDac3d8ACeAB7BFfDDFA18877A4c5D5Aa82","ZEUS_DEPLOYED_StrategyBase_Beacon":"0xf2c2AcA859C685895E60ca7A14274365b64c0c2a","ZEUS_DEPLOYED_StrategyBase_Impl":"0x3Dc582A90b920AA6aa0204e0517d6767C9C8c268","ZEUS_DEPLOYED_StrategyManager_Proxy":"0xF9fbF2e35D8803273E214c99BF15174139f4E67a","ZEUS_DEPLOYED_StrategyManager_Impl":"0x158E6FBFb5FAc98894BF0Da28998efcB3Ba1C58A","ZEUS_DEPLOYED_StrategyFactory_Proxy":"0xad4a89e3ca9b3dc25aabe0aa7d72e61d2ec66052","ZEUS_DEPLOYED_StrategyFactory_Impl":"0x203654Ea1e39d00983A9cE0457B2d2efC188fC40","ZEUS_DEPLOYED_StrategyBaseTVLLimits_Impl":"0x98bD5748dc964e85400B3A5D0152c444201C577a","ZEUS_DEPLOYED_PermissionController_Impl":"0x259597c0AEc3c9978D24e892225f2F4Ac142d885","ZEUS_DEPLOYED_PermissionController_Proxy":"0xa2348c77802238Db39f0CefAa500B62D3FDD682b","ZEUS_DEPLOYED_AllocationManager_Impl":"0xc7618DC8607503C15f4393782068B79655104A24","ZEUS_DEPLOYED_AllocationManager_Proxy":"0xFdD5749e11977D60850E06bF5B13221Ad95eb6B4","ZEUS_DEPLOYED_StrategyBaseTVLLimits_0":"0x6e5d5060b33ca2090a78e9cb74fe207453b30e49","ZEUS_DEPLOYED_StrategyBaseTVLLimits_1":"0xf6a09ae03d7760aecf1626ce7df0f113bec2d9bd","ZEUS_DEPLOYED_StrategyBaseTVLLimits_2":"0x7fa77c321bf66e42eabc9b10129304f7f90c5585","ZEUS_DEPLOYED_StrategyBaseTVLLimits_3":"0x24da526f9e465c4fb6bae41e226df8aa5b34eac7","ZEUS_DEPLOYED_StrategyBaseTVLLimits_4":"0x6dc6ce589f852f96ac86cb160ab0b15b9f56dedd","ZEUS_DEPLOYED_StrategyBaseTVLLimits_5":"0x3c28437e610fb099cc3d6de4d9c707dfacd308ae","ZEUS_DEPLOYED_StrategyBaseTVLLimits_6":"0x7b6257f5caf7311b36f7416133a8499c68a83c3a","ZEUS_DEPLOYED_StrategyBaseTVLLimits_7":"0xc86382179500e8ed3e686fc4a99ed9ec72df3f56","ZEUS_DEPLOYED_StrategyBaseTVLLimits_8":"0x3cb1fd19cfb178c1098f2fc1e11090a0642b2314","ZEUS_DEPLOYED_StrategyBaseTVLLimits_9":"0x87f6c7d24b109919eb38295e3f8298425e6331d9","ZEUS_DEPLOYED_StrategyBaseTVLLimits_10":"0x5c8b55722f421556a2aafb7a3ea63d4c3e514312","ZEUS_DEPLOYED_StrategyBaseTVLLimits_11":"0xd523267698c81a372191136e477fdebfa33d9fb4"} diff --git a/script/utils/BN256G2.sol b/script/utils/BN256G2.sol new file mode 100644 index 00000000..45cd34b2 --- /dev/null +++ b/script/utils/BN256G2.sol @@ -0,0 +1,337 @@ +pragma solidity ^0.8.0; + +/** + * @title Elliptic curve operations on twist points for alt_bn128 + * @author Mustafa Al-Bassam (mus@musalbas.com) + * @dev Homepage: https://github.com/musalbas/solidity-BN256G2 + */ +library BN256G2 { + uint256 internal constant FIELD_MODULUS = + 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; + uint256 internal constant TWISTBX = + 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; + uint256 internal constant TWISTBY = + 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2; + uint256 internal constant PTXX = 0; + uint256 internal constant PTXY = 1; + uint256 internal constant PTYX = 2; + uint256 internal constant PTYY = 3; + uint256 internal constant PTZX = 4; + uint256 internal constant PTZY = 5; + + /** + * @notice Add two twist points + * @param pt1xx Coefficient 1 of x on point 1 + * @param pt1xy Coefficient 2 of x on point 1 + * @param pt1yx Coefficient 1 of y on point 1 + * @param pt1yy Coefficient 2 of y on point 1 + * @param pt2xx Coefficient 1 of x on point 2 + * @param pt2xy Coefficient 2 of x on point 2 + * @param pt2yx Coefficient 1 of y on point 2 + * @param pt2yy Coefficient 2 of y on point 2 + * @return (pt3xx, pt3xy, pt3yx, pt3yy) + */ + function ECTwistAdd( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy + ) public view returns (uint256, uint256, uint256, uint256) { + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + if (!(pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0)) { + assert(_isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy)); + } + return (pt2xx, pt2xy, pt2yx, pt2yy); + } else if (pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0) { + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + return (pt1xx, pt1xy, pt1yx, pt1yy); + } + + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + assert(_isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy)); + + uint256[6] memory pt3 = + _ECTwistAddJacobian(pt1xx, pt1xy, pt1yx, pt1yy, 1, 0, pt2xx, pt2xy, pt2yx, pt2yy, 1, 0); + + return _fromJacobian(pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]); + } + + /** + * @notice Multiply a twist point by a scalar + * @param s Scalar to multiply by + * @param pt1xx Coefficient 1 of x + * @param pt1xy Coefficient 2 of x + * @param pt1yx Coefficient 1 of y + * @param pt1yy Coefficient 2 of y + * @return (pt2xx, pt2xy, pt2yx, pt2yy) + */ + function ECTwistMul( + uint256 s, + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy + ) public view returns (uint256, uint256, uint256, uint256) { + uint256 pt1zx = 1; + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + pt1xx = 1; + pt1yx = 1; + pt1zx = 0; + } else { + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + } + + uint256[6] memory pt2 = _ECTwistMulJacobian(s, pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, 0); + + return _fromJacobian(pt2[PTXX], pt2[PTXY], pt2[PTYX], pt2[PTYY], pt2[PTZX], pt2[PTZY]); + } + + /** + * @notice Get the field modulus + * @return The field modulus + */ + function GetFieldModulus() public pure returns (uint256) { + return FIELD_MODULUS; + } + + function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) { + return addmod(a, n - b, n); + } + + function _FQ2Mul( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256, uint256) { + return ( + submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS), + addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS) + ); + } + + function _FQ2Muc(uint256 xx, uint256 xy, uint256 c) internal pure returns (uint256, uint256) { + return (mulmod(xx, c, FIELD_MODULUS), mulmod(xy, c, FIELD_MODULUS)); + } + + function _FQ2Add( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256, uint256) { + return (addmod(xx, yx, FIELD_MODULUS), addmod(xy, yy, FIELD_MODULUS)); + } + + function _FQ2Sub( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256 rx, uint256 ry) { + return (submod(xx, yx, FIELD_MODULUS), submod(xy, yy, FIELD_MODULUS)); + } + + function _FQ2Div( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal view returns (uint256, uint256) { + (yx, yy) = _FQ2Inv(yx, yy); + return _FQ2Mul(xx, xy, yx, yy); + } + + function _FQ2Inv(uint256 x, uint256 y) internal view returns (uint256, uint256) { + uint256 inv = _modInv( + addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), + FIELD_MODULUS + ); + return (mulmod(x, inv, FIELD_MODULUS), FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS)); + } + + function _isOnCurve( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (bool) { + uint256 yyx; + uint256 yyy; + uint256 xxxx; + uint256 xxxy; + (yyx, yyy) = _FQ2Mul(yx, yy, yx, yy); + (xxxx, xxxy) = _FQ2Mul(xx, xy, xx, xy); + (xxxx, xxxy) = _FQ2Mul(xxxx, xxxy, xx, xy); + (yyx, yyy) = _FQ2Sub(yyx, yyy, xxxx, xxxy); + (yyx, yyy) = _FQ2Sub(yyx, yyy, TWISTBX, TWISTBY); + return yyx == 0 && yyy == 0; + } + + function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) { + bool success; + assembly ("memory-safe") { + let freemem := mload(0x40) + mstore(freemem, 0x20) + mstore(add(freemem, 0x20), 0x20) + mstore(add(freemem, 0x40), 0x20) + mstore(add(freemem, 0x60), a) + mstore(add(freemem, 0x80), sub(n, 2)) + mstore(add(freemem, 0xA0), n) + success := staticcall(sub(gas(), 2000), 5, freemem, 0xC0, freemem, 0x20) + result := mload(freemem) + } + require(success); + } + + function _fromJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) internal view returns (uint256 pt2xx, uint256 pt2xy, uint256 pt2yx, uint256 pt2yy) { + uint256 invzx; + uint256 invzy; + (invzx, invzy) = _FQ2Inv(pt1zx, pt1zy); + (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, invzx, invzy); + (pt2yx, pt2yy) = _FQ2Mul(pt1yx, pt1yy, invzx, invzy); + } + + function _ECTwistAddJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy, + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy, + uint256 pt2zx, + uint256 pt2zy + ) internal pure returns (uint256[6] memory pt3) { + if (pt1zx == 0 && pt1zy == 0) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + (pt2xx, pt2xy, pt2yx, pt2yy, pt2zx, pt2zy); + return pt3; + } else if (pt2zx == 0 && pt2zy == 0) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + return pt3; + } + + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // U1 = y2 * z1 + (pt3[PTYX], pt3[PTYY]) = _FQ2Mul(pt1yx, pt1yy, pt2zx, pt2zy); // U2 = y1 * z2 + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // V1 = x2 * z1 + (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1xx, pt1xy, pt2zx, pt2zy); // V2 = x1 * z2 + + if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) { + if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + return pt3; + } + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = (1, 0, 1, 0, 0, 0); + return pt3; + } + + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // W = z1 * z2 + (pt1xx, pt1xy) = _FQ2Sub(pt2yx, pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2 + (pt1yx, pt1yy) = _FQ2Sub(pt2xx, pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2 + (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1yx, pt1yy); // V_squared = V * V + (pt2yx, pt2yy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2 + (pt1zx, pt1zy) = _FQ2Mul(pt1zx, pt1zy, pt1yx, pt1yy); // V_cubed = V * V_squared + (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // newz = V_cubed * W + (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, pt1xx, pt1xy); // U * U + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // U * U * W + (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt1zx, pt1zy); // U * U * W - V_cubed + (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 2); // 2 * V_squared_times_V2 + (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt2zx, pt2zy); // A = U * U * W - V_cubed - 2 * V_squared_times_V2 + (pt3[PTXX], pt3[PTXY]) = _FQ2Mul(pt1yx, pt1yy, pt2xx, pt2xy); // newx = V * A + (pt1yx, pt1yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // V_squared_times_V2 - A + (pt1yx, pt1yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // U * (V_squared_times_V2 - A) + (pt1xx, pt1xy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2 + (pt3[PTYX], pt3[PTYY]) = _FQ2Sub(pt1yx, pt1yy, pt1xx, pt1xy); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2 + } + + function _ECTwistDoubleJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) + internal + pure + returns ( + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy, + uint256 pt2zx, + uint256 pt2zy + ) + { + (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 3); // 3 * x + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x + (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z + (pt2yx, pt2yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S + (pt1xx, pt1xy) = _FQ2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W + (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 8); // 8 * B + (pt1xx, pt1xy) = _FQ2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S + (pt2yx, pt2yy) = _FQ2Muc(pt2yx, pt2yy, 4); // 4 * B + (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H) + (pt2xx, pt2xy) = _FQ2Muc(pt1yx, pt1yy, 8); // 8 * y + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared + (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared + (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 2); // 2 * H + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared + (pt2zx, pt2zy) = _FQ2Muc(pt2zx, pt2zy, 8); // newz = 8 * S * S_squared + } + + function _ECTwistMulJacobian( + uint256 d, + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) internal pure returns (uint256[6] memory pt2) { + while (d != 0) { + if ((d & 1) != 0) { + pt2 = _ECTwistAddJacobian( + pt2[PTXX], + pt2[PTXY], + pt2[PTYX], + pt2[PTYY], + pt2[PTZX], + pt2[PTZY], + pt1xx, + pt1xy, + pt1yx, + pt1yy, + pt1zx, + pt1zy + ); + } + (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy) = + _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + + d = d / 2; + } + } +} \ No newline at end of file diff --git a/script/utils/CoreDeploymentLib.sol b/script/utils/CoreDeploymentLib.sol new file mode 100644 index 00000000..cb74f0b4 --- /dev/null +++ b/script/utils/CoreDeploymentLib.sol @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Vm} from "forge-std/Vm.sol"; +import {console2 as console} from "forge-std/Test.sol"; +import {stdJson} from "forge-std/StdJson.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; + +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; +import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; +import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; +import {StrategyBaseTVLLimits} from "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol"; +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; +import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; + +library CoreDeploymentLib { + using stdJson for string; + + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct AVSDirectoryConfig { + uint256 initialPausedStatus; + } + + struct StrategyManagerConfig { + uint256 initPausedStatus; + uint256 initWithdrawalDelayBlocks; + } + + struct SlasherConfig { + uint256 initPausedStatus; + } + + struct DelegationManagerConfig { + uint256 initPausedStatus; + uint256 withdrawalDelayBlocks; + uint256 globalOperatorCommissionBips; + } + + struct EigenPodManagerConfig { + uint256 initPausedStatus; + uint64 genesisTime; + } + + struct RewardsCoordinatorConfig { + address rewardsUpdater; + uint256 initPausedStatus; + uint256 maxRewardsDuration; + uint256 maxRetroactiveLength; + uint256 maxFutureLength; + uint256 genesisRewardsTimestamp; + uint256 defaultOperatorSplitBips; + address updater; + uint256 activationDelay; + uint256 calculationIntervalSeconds; + uint256 globalOperatorCommissionBips; + } + + struct StrategyFactoryConfig { + uint256 initPausedStatus; + } + + struct DeploymentConfig { + StrategyManagerConfig strategyManager; + AVSDirectoryConfig avsDirectory; + SlasherConfig slasher; + DelegationManagerConfig delegationManager; + EigenPodManagerConfig eigenPodManager; + RewardsCoordinatorConfig rewardsCoordinator; + StrategyFactoryConfig strategyFactory; + } + + struct DeploymentData { + address delegationManager; + address avsDirectory; + address allocationManager; + address strategyManager; + address eigenPodManager; + address rewardsCoordinator; + address eigenPodBeacon; + address pauserRegistry; + address strategyFactory; + address strategyBeacon; + address eigenStrategy; + address eigen; + address backingEigen; + address permissionController; + } + + struct DeploymentAddresses { + address delegationManagerImpl; + address avsDirectoryImpl; + address strategyManagerImpl; + address strategyFactoryImpl; + address eigenPodManagerImpl; + address rewardsCoordinatorImpl; + address eigenPodImpl; + address eigenPodBeaconImpl; + address baseStrategyImpl; + address pauserRegistryImpl; + } + + function deployCoreFromScratch( + address proxyAdmin, + DeploymentConfig memory config + ) internal returns (DeploymentData memory) { + DeploymentData memory result; + DeploymentAddresses memory addrs; + + // Deploy empty proxies + result = _deployEmptyProxies(proxyAdmin, result); + + // Deploy implementations + ( + addrs.delegationManagerImpl, + addrs.avsDirectoryImpl, + addrs.strategyManagerImpl, + addrs.strategyFactoryImpl + ) = _deployMainImplementations(result, config); + + address ethPOSDeposit = _getEthPOSDeposit(); + + ( + addrs.eigenPodManagerImpl, + addrs.rewardsCoordinatorImpl, + addrs.eigenPodImpl, + addrs.eigenPodBeaconImpl, + addrs.baseStrategyImpl, + addrs.pauserRegistryImpl + ) = _deployRemainingImplementations(result, config, ethPOSDeposit); + + // Deploy strategy beacon + result.strategyBeacon = address(new UpgradeableBeacon(addrs.baseStrategyImpl)); + + // Upgrade all contracts + _upgradeAllContracts( + result, + config, + proxyAdmin, + addrs + ); + + return result; + } + + function _deployEmptyProxies(address proxyAdmin, DeploymentData memory result) private returns (DeploymentData memory) { + result.delegationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.avsDirectory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.strategyManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.eigenPodManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.rewardsCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.eigenPodBeacon = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.pauserRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.strategyFactory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + return result; + } + + function _deployMainImplementations(DeploymentData memory result, DeploymentConfig memory config) private returns ( + address delegationManagerImpl, + address avsDirectoryImpl, + address strategyManagerImpl, + address strategyFactoryImpl + ) { + delegationManagerImpl = address( + new DelegationManager( + IStrategyManager(result.strategyManager), + IEigenPodManager(result.eigenPodManager), + IAllocationManager(result.allocationManager), + IPauserRegistry(result.pauserRegistry), + IPermissionController(result.permissionController), + uint32(config.delegationManager.initPausedStatus) + ) + ); + + avsDirectoryImpl = address( + new AVSDirectory( + IDelegationManager(result.delegationManager), + IPauserRegistry(result.pauserRegistry) + ) + ); + + strategyManagerImpl = address( + new StrategyManager( + IDelegationManager(result.delegationManager), + IPauserRegistry(result.pauserRegistry) + ) + ); + + strategyFactoryImpl = address(new StrategyFactory( + IStrategyManager(result.strategyManager), + IPauserRegistry(result.pauserRegistry) + )); + + return (delegationManagerImpl, avsDirectoryImpl, strategyManagerImpl, strategyFactoryImpl); + } + + function _getEthPOSDeposit() private view returns (address) { + if (block.chainid == 1) { + return 0x00000000219ab540356cBB839Cbe05303d7705Fa; + } + // Handle non-mainnet chains + /// TODO: Handle Eth pos + return address(0); + } + + function _deployRemainingImplementations( + DeploymentData memory result, + DeploymentConfig memory config, + address ethPOSDeposit + ) private returns ( + address eigenPodManagerImpl, + address rewardsCoordinatorImpl, + address eigenPodImpl, + address eigenPodBeaconImpl, + address baseStrategyImpl, + address pauserRegistryImpl + ) { + eigenPodManagerImpl = address( + new EigenPodManager( + IETHPOSDeposit(ethPOSDeposit), + IBeacon(result.eigenPodBeacon), + IDelegationManager(result.delegationManager), + IPauserRegistry(result.pauserRegistry) + ) + ); + + rewardsCoordinatorImpl = address( + new RewardsCoordinator( + IDelegationManager(result.delegationManager), + IStrategyManager(result.strategyManager), + IAllocationManager(result.allocationManager), + IPauserRegistry(result.pauserRegistry), + IPermissionController(result.permissionController), + uint32(config.rewardsCoordinator.calculationIntervalSeconds), + uint32(config.rewardsCoordinator.maxRewardsDuration), + uint32(config.rewardsCoordinator.maxRetroactiveLength), + uint32(config.rewardsCoordinator.maxFutureLength), + uint32(config.rewardsCoordinator.genesisRewardsTimestamp) + ) + ); + + eigenPodImpl = address( + new EigenPod( + IETHPOSDeposit(ethPOSDeposit), + IEigenPodManager(result.eigenPodManager), + config.eigenPodManager.genesisTime + ) + ); + + eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); + baseStrategyImpl = address(new StrategyBase(IStrategyManager(result.strategyManager), IPauserRegistry(result.pauserRegistry))); + + /// TODO: PauserRegistry isn't upgradeable + pauserRegistryImpl = address( + new PauserRegistry( + new address[](0), // Empty array for pausers + result.permissionController // ProxyAdmin as the unpauser + ) + ); + + return ( + eigenPodManagerImpl, + rewardsCoordinatorImpl, + eigenPodImpl, + eigenPodBeaconImpl, + baseStrategyImpl, + pauserRegistryImpl + ); + } + + function _upgradeAllContracts( + DeploymentData memory result, + DeploymentConfig memory config, + address proxyAdmin, + DeploymentAddresses memory addrs + ) private { + bytes memory upgradeCall; + + upgradeCall = abi.encodeCall( + DelegationManager.initialize, + (proxyAdmin, config.delegationManager.initPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall(result.delegationManager, addrs.delegationManagerImpl, upgradeCall); + + upgradeCall = abi.encodeCall( + StrategyManager.initialize, + (proxyAdmin, result.strategyFactory, config.strategyManager.initPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall(result.strategyManager, addrs.strategyManagerImpl, upgradeCall); + + upgradeCall = abi.encodeCall( + StrategyFactory.initialize, + (proxyAdmin, config.strategyFactory.initPausedStatus, IBeacon(result.strategyBeacon)) + ); + UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, addrs.strategyFactoryImpl, upgradeCall); + + upgradeCall = abi.encodeCall( + EigenPodManager.initialize, + (proxyAdmin, config.eigenPodManager.initPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, addrs.eigenPodManagerImpl, upgradeCall); + + upgradeCall = abi.encodeCall( + AVSDirectory.initialize, + (proxyAdmin, config.avsDirectory.initialPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, addrs.avsDirectoryImpl, upgradeCall); + + upgradeCall = abi.encodeCall( + RewardsCoordinator.initialize, + ( + proxyAdmin, + config.rewardsCoordinator.initPausedStatus, + config.rewardsCoordinator.rewardsUpdater, + uint32(config.rewardsCoordinator.activationDelay), + uint16(config.rewardsCoordinator.defaultOperatorSplitBips) + ) + ); + UpgradeableProxyLib.upgradeAndCall(result.rewardsCoordinator, addrs.rewardsCoordinatorImpl, upgradeCall); + + upgradeCall = abi.encodeCall(EigenPod.initialize, (address(result.eigenPodManager))); + UpgradeableProxyLib.upgradeAndCall(result.eigenPodBeacon, addrs.eigenPodImpl, upgradeCall); + } + + function readCoreDeploymentJson(string memory path, uint256 chainId) internal returns (CoreDeploymentLib.DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), ".json")); + return parseZeusJson(filePath); + } + + function readCoreDeploymentJson(string memory path, uint256 chainId, string memory environment) internal returns (CoreDeploymentLib.DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), "-", environment, ".json")); + return parseZeusJson(filePath); + } + + function parseZeusJson(string memory filePath) internal returns (CoreDeploymentLib.DeploymentData memory) { + string memory json = vm.readFile(filePath); + require(vm.exists(filePath), "Deployment file does not exist"); + CoreDeploymentLib.DeploymentData memory deploymentData; + + deploymentData.delegationManager = json.readAddress(".ZEUS_DEPLOYED_DelegationManager_Proxy"); + deploymentData.avsDirectory = json.readAddress(".ZEUS_DEPLOYED_AVSDirectory_Proxy"); + deploymentData.allocationManager = json.readAddress(".ZEUS_DEPLOYED_AllocationManager_Proxy"); + deploymentData.strategyManager = json.readAddress(".ZEUS_DEPLOYED_StrategyManager_Proxy"); + deploymentData.eigenPodManager = json.readAddress(".ZEUS_DEPLOYED_EigenPodManager_Proxy"); + deploymentData.rewardsCoordinator = json.readAddress(".ZEUS_DEPLOYED_RewardsCoordinator_Proxy"); + deploymentData.eigenPodBeacon = json.readAddress(".ZEUS_DEPLOYED_EigenPod_Beacon"); + deploymentData.pauserRegistry = json.readAddress(".ZEUS_DEPLOYED_PauserRegistry_Impl"); + deploymentData.strategyFactory = json.readAddress(".ZEUS_DEPLOYED_StrategyFactory_Proxy"); + deploymentData.strategyBeacon = json.readAddress(".ZEUS_DEPLOYED_StrategyBase_Beacon"); + deploymentData.eigenStrategy = json.readAddress(".ZEUS_DEPLOYED_EigenStrategy_Proxy"); + deploymentData.eigen = json.readAddress(".ZEUS_DEPLOYED_Eigen_Proxy"); + deploymentData.backingEigen = json.readAddress(".ZEUS_DEPLOYED_BackingEigen_Proxy"); + deploymentData.permissionController = json.readAddress(".ZEUS_DEPLOYED_PermissionController_Proxy"); + + return deploymentData; + } +} \ No newline at end of file diff --git a/script/utils/MiddlewareDeploymentLib.sol b/script/utils/MiddlewareDeploymentLib.sol new file mode 100644 index 00000000..d5ca7844 --- /dev/null +++ b/script/utils/MiddlewareDeploymentLib.sol @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Vm} from "forge-std/Vm.sol"; +import {console2 as console} from "forge-std/Test.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "../../src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; +import {ServiceManagerMock} from "../../test/mocks/ServiceManagerMock.sol"; +import {PauserRegistry, IPauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract ERC20Mock is ERC20 { + constructor() ERC20("", "") {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } +} + +library MiddlewareDeploymentLib { + using stdJson for *; + using Strings for *; + using UpgradeableProxyLib for address; + + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct DeploymentData { + address registryCoordinator; + address serviceManager; + address operatorStateRetriever; + address blsapkRegistry; + address indexRegistry; + address stakeRegistry; + address strategy; + address token; + address pauserRegistry; + } + + struct ConfigData { + address admin; + uint256 numQuorums; + uint256[] operatorParams; + } + + struct ImplementationAddresses { + address serviceManagerImpl; + address stakeRegistryImpl; + address blsApkRegistryImpl; + address indexRegistryImpl; + address registryCoordinatorImpl; + } + + struct QuorumParams { + IRegistryCoordinator.OperatorSetParam[] quorumsOperatorSetParams; + uint96[] quorumsMinimumStake; + IStakeRegistry.StrategyParams[][] quorumsStrategyParams; + } + + function deployContracts( + address proxyAdmin, + CoreDeploymentLib.DeploymentData memory core, + ConfigData memory config + ) internal returns (DeploymentData memory) { + DeploymentData memory result; + + // Deploy pauser registry + result.pauserRegistry = _deployPauserRegistry(config.admin); + + // Deploy proxies + result.stakeRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.blsapkRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.serviceManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + + // Deploy operator state retriever + result.operatorStateRetriever = address(new OperatorStateRetriever()); + + // Deploy token and strategy + (result.token, result.strategy) = _deployTokenAndStrategy(core.strategyFactory); + + return result; + } + + function upgradeContracts( + DeploymentData memory deployment, + ConfigData memory config, + CoreDeploymentLib.DeploymentData memory core + ) internal { + // Deploy implementation contracts + ImplementationAddresses memory impls = _deployImplementations(deployment, core); + + // Prepare upgrade data + ( + bytes memory registryCoordinatorUpgradeCall, + bytes memory serviceManagerUpgradeCall + ) = _prepareUpgradeCalls(deployment, config); + + // Execute upgrades + _executeUpgrades( + deployment, + impls, + registryCoordinatorUpgradeCall, + serviceManagerUpgradeCall + ); + } + + function _deployPauserRegistry(address admin) private returns (address) { + address[] memory pausers = new address[](2); + pausers[0] = admin; + pausers[1] = admin; + return address(new PauserRegistry(pausers, admin)); + } + + function _deployTokenAndStrategy(address strategyFactory) private returns (address token, address strategy) { + ERC20Mock tokenContract = new ERC20Mock(); + token = address(tokenContract); + strategy = address(IStrategyFactory(strategyFactory).deployNewStrategy(IERC20(token))); + } + + function _deployImplementations( + DeploymentData memory deployment, + CoreDeploymentLib.DeploymentData memory core + ) private returns (ImplementationAddresses memory impls) { + impls.serviceManagerImpl = address( + new ServiceManagerMock( + IAVSDirectory(core.avsDirectory), + IRewardsCoordinator(core.rewardsCoordinator), + IRegistryCoordinator(deployment.registryCoordinator), + IStakeRegistry(deployment.stakeRegistry), + IAllocationManager(core.allocationManager) + ) + ); + + impls.stakeRegistryImpl = address( + new StakeRegistry( + IRegistryCoordinator(deployment.registryCoordinator), + IDelegationManager(core.delegationManager), + IAVSDirectory(core.avsDirectory), + IServiceManager(deployment.serviceManager) + ) + ); + + impls.blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(deployment.registryCoordinator))); + impls.indexRegistryImpl = address(new IndexRegistry(IRegistryCoordinator(deployment.registryCoordinator))); + + impls.registryCoordinatorImpl = address( + new RegistryCoordinator( + IServiceManager(deployment.serviceManager), + IStakeRegistry(deployment.stakeRegistry), + IBLSApkRegistry(deployment.blsapkRegistry), + IIndexRegistry(deployment.indexRegistry), + IPauserRegistry(deployment.pauserRegistry) + ) + ); + } + + function _prepareUpgradeCalls( + DeploymentData memory deployment, + ConfigData memory config + ) private pure returns (bytes memory registryCoordinatorUpgradeCall, bytes memory serviceManagerUpgradeCall) { + IStrategy[1] memory deployedStrategyArray = [IStrategy(deployment.strategy)]; + + QuorumParams memory params = _prepareQuorumParams(config, deployedStrategyArray); + + registryCoordinatorUpgradeCall = abi.encodeCall( + RegistryCoordinator.initialize, + ( + config.admin, + config.admin, + config.admin, + uint256(0), + params.quorumsOperatorSetParams, + params.quorumsMinimumStake, + params.quorumsStrategyParams, + new StakeType[](1), + new uint32[](1) + ) + ); + + serviceManagerUpgradeCall = abi.encodeCall( + ServiceManagerMock.initialize, + (config.admin, config.admin, config.admin) + ); + } + + function _prepareQuorumParams( + ConfigData memory config, + IStrategy[1] memory deployedStrategyArray + ) private pure returns (QuorumParams memory params) { + uint256 numQuorums = config.numQuorums; + uint256 numStrategies = deployedStrategyArray.length; + + params.quorumsOperatorSetParams = new IRegistryCoordinator.OperatorSetParam[](numQuorums); + uint256[] memory operator_params = config.operatorParams; + + for (uint256 i = 0; i < numQuorums; i++) { + params.quorumsOperatorSetParams[i] = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: uint32(operator_params[i]), + kickBIPsOfOperatorStake: uint16(operator_params[i + 1]), + kickBIPsOfTotalStake: uint16(operator_params[i + 2]) + }); + } + + params.quorumsMinimumStake = new uint96[](numQuorums); + params.quorumsStrategyParams = new IStakeRegistry.StrategyParams[][](numQuorums); + + for (uint256 i = 0; i < numQuorums; i++) { + params.quorumsStrategyParams[i] = new IStakeRegistry.StrategyParams[](numStrategies); + for (uint256 j = 0; j < numStrategies; j++) { + params.quorumsStrategyParams[i][j] = IStakeRegistry.StrategyParams({ + strategy: deployedStrategyArray[j], + multiplier: 1 ether + }); + } + } + } + + function _executeUpgrades( + DeploymentData memory deployment, + ImplementationAddresses memory impls, + bytes memory registryCoordinatorUpgradeCall, + bytes memory serviceManagerUpgradeCall + ) private { + UpgradeableProxyLib.upgradeAndCall(deployment.serviceManager, impls.serviceManagerImpl, serviceManagerUpgradeCall); + UpgradeableProxyLib.upgrade(deployment.stakeRegistry, impls.stakeRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.blsapkRegistry, impls.blsApkRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.indexRegistry, impls.indexRegistryImpl); + UpgradeableProxyLib.upgradeAndCall(deployment.registryCoordinator, impls.registryCoordinatorImpl, registryCoordinatorUpgradeCall); + } + + function upgradeContracts( + DeploymentData memory deployment, + CoreDeploymentLib.DeploymentData memory core + ) internal { + ImplementationAddresses memory impls = _deployImplementations(deployment, core); + + _executeUpgrades(deployment, impls); + } + + function _executeUpgrades( + DeploymentData memory deployment, + ImplementationAddresses memory impls + ) private { + UpgradeableProxyLib.upgrade(deployment.serviceManager, impls.serviceManagerImpl); + UpgradeableProxyLib.upgrade(deployment.stakeRegistry, impls.stakeRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.blsapkRegistry, impls.blsApkRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.indexRegistry, impls.indexRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.registryCoordinator, impls.registryCoordinatorImpl); + } + + function readDeploymentJson(string memory path, uint256 chainId) internal returns (DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), ".json")); + return parseDeploymentJson(filePath); + } + + function readDeploymentJson(string memory path, uint256 chainId, string memory environment) internal returns (DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), "-", environment, ".json")); + return parseDeploymentJson(filePath); + } + + function parseDeploymentJson(string memory filePath) internal returns (DeploymentData memory) { + string memory json = vm.readFile(filePath); + require(vm.exists(filePath), "Deployment file does not exist"); + DeploymentData memory deploymentData; + + deploymentData.serviceManager = json.readAddress(".serviceManager"); + deploymentData.registryCoordinator = json.readAddress(".registryCoordinator"); + deploymentData.blsapkRegistry = json.readAddress(".blsapkRegistry"); + deploymentData.indexRegistry = json.readAddress(".indexRegistry"); + deploymentData.stakeRegistry = json.readAddress(".stakeRegistry"); + deploymentData.operatorStateRetriever = json.readAddress(".operatorStateRetriever"); + deploymentData.token = json.readAddress(".token"); + deploymentData.strategy = json.readAddress(".strategy"); + + return deploymentData; + } +} \ No newline at end of file diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol new file mode 100644 index 00000000..c42c85b1 --- /dev/null +++ b/script/utils/OperatorLib.sol @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Vm} from "forge-std/Vm.sol"; + +import {console2 as console} from "forge-std/Test.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {ERC20Mock} from "./MiddlewareDeploymentLib.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {BN256G2} from "./BN256G2.sol"; + + +library OperatorLib { + using BN254 for *; + using Strings for uint256; + + struct Wallet { + uint256 privateKey; + address addr; + } + + struct BLSWallet { + uint256 privateKey; + BN254.G2Point publicKeyG2; + BN254.G1Point publicKeyG1; + } + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct Operator { + Wallet key; + BLSWallet signingKey; + } + + function createBLSWallet(uint256 salt) internal returns (BLSWallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); + BN254.G1Point memory publicKeyG1 = BN254.generatorG1().scalar_mul(privateKey); + BN254.G2Point memory publicKeyG2 = mul(privateKey); + + return BLSWallet({ + privateKey: privateKey, + publicKeyG2: publicKeyG2, + publicKeyG1: publicKeyG1 + }); + } + + function createWallet(uint256 salt) internal pure returns (Wallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); + address addr = vm.addr(privateKey); + + return Wallet({ + privateKey: privateKey, + addr: addr + }); + } + + function createOperator(string memory name) internal returns (Operator memory) { + uint256 salt = uint256(keccak256(abi.encodePacked(name))); + Wallet memory vmWallet = createWallet(salt); + BLSWallet memory blsWallet = createBLSWallet(salt); + + return Operator({ + key: vmWallet, + signingKey: blsWallet + }); + } + + + function mul(uint256 x) internal returns (BN254.G2Point memory g2Point) { + string[] memory inputs = new string[](5); + inputs[0] = "go"; + inputs[1] = "run"; + inputs[2] = "test/ffi/go/g2mul.go"; + inputs[3] = x.toString(); + + inputs[4] = "1"; + bytes memory res = vm.ffi(inputs); + g2Point.X[1] = abi.decode(res, (uint256)); + + inputs[4] = "2"; + res = vm.ffi(inputs); + g2Point.X[0] = abi.decode(res, (uint256)); + + inputs[4] = "3"; + res = vm.ffi(inputs); + g2Point.Y[1] = abi.decode(res, (uint256)); + + inputs[4] = "4"; + res = vm.ffi(inputs); + g2Point.Y[0] = abi.decode(res, (uint256)); + } + + function signWithOperatorKey( + Operator memory operator, + bytes32 digest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.key.privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function signWithSigningKey( + Operator memory operator, + bytes32 digest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.signingKey.privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function aggregate( + BN254.G2Point memory pk1, + BN254.G2Point memory pk2 + ) internal view returns (BN254.G2Point memory apk) { + (apk.X[0], apk.X[1], apk.Y[0], apk.Y[1]) = + BN256G2.ECTwistAdd(pk1.X[0], pk1.X[1], pk1.Y[0], pk1.Y[1], pk2.X[0], pk2.X[1], pk2.Y[0], pk2.Y[1]); + } + + function mintMockTokens(Operator memory operator, address token, uint256 amount) internal { + ERC20Mock(token).mint(operator.key.addr, amount); + } + + function depositTokenIntoStrategy( + Operator memory, + address strategyManager, + address strategy, + address token, + uint256 amount + ) internal returns (uint256) { + /// TODO :make sure strategy associated with token + IStrategy strategy = IStrategy(strategy); + require(address(strategy) != address(0), "Strategy was not found"); + IStrategyManager strategyManager = IStrategyManager(strategyManager); + + ERC20Mock(token).approve(address(strategyManager), amount); + uint256 shares = strategyManager.depositIntoStrategy(strategy, IERC20(token), amount); + + return shares; + } + + function registerAsOperator( + Operator memory operator, + address delegationManager + ) internal { + IDelegationManager delegationManagerInstance = IDelegationManager(delegationManager); + + delegationManagerInstance.registerAsOperator( + operator.key.addr, + 0, + "" + ); + } + + function registerOperatorToAVS_M2( + Operator memory operator, + address avsDirectory, + address serviceManager, + address registryCoordinator, + bytes memory quorumNumbers, + string memory socket + ) internal { + IAVSDirectory avsDirectoryInstance = IAVSDirectory(avsDirectory); + RegistryCoordinator registryCoordinatorInstance = RegistryCoordinator(registryCoordinator); + + bytes32 salt = keccak256(abi.encodePacked(block.timestamp, operator.key.addr)); + uint256 expiry = block.timestamp + 1 hours; + + bytes32 operatorRegistrationDigestHash = avsDirectoryInstance + .calculateOperatorAVSRegistrationDigestHash( + operator.key.addr, serviceManager, salt, expiry + ); + + bytes memory signature = signWithOperatorKey(operator, operatorRegistrationDigestHash); + // Get the pubkey registration message hash that needs to be signed + bytes32 pubkeyRegistrationMessageHash = registryCoordinatorInstance.calculatePubkeyRegistrationMessageHash(operator.key.addr); + + // Sign the pubkey registration message hash + BN254.G1Point memory blsSig = signMessage(operator.signingKey, pubkeyRegistrationMessageHash); + + IBLSApkRegistry.PubkeyRegistrationParams memory params = IBLSApkRegistry.PubkeyRegistrationParams({ + pubkeyG1: operator.signingKey.publicKeyG1, + pubkeyG2: operator.signingKey.publicKeyG2, + pubkeyRegistrationSignature: blsSig + }); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = ISignatureUtils + .SignatureWithSaltAndExpiry({signature: signature, salt: salt, expiry: expiry}); + + // Call the registerOperator function on the registry + registryCoordinatorInstance.registerOperator(quorumNumbers, socket, params, operatorSignature); + } + + function deregisterOperatorFromAVS_M2(Operator memory operator, address registryCoordinator) internal { + vm.prank(operator.key.addr); + RegistryCoordinator(registryCoordinator).deregisterOperator(""); + } + + function registerOperatorFromAVS_OpSet( + Operator memory operator, + address allocationManager, + address registryCoordinator, + address avs, + uint32[] memory operatorSetIds + ) internal { + + bytes memory registrationParamsData; + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + + // Get the pubkey registration message hash that needs to be signed + bytes32 pubkeyRegistrationMessageHash = RegistryCoordinator(registryCoordinator).calculatePubkeyRegistrationMessageHash(operator.key.addr); + + // Sign the pubkey registration message hash + BN254.G1Point memory signature = signMessage(operator.signingKey, pubkeyRegistrationMessageHash); + + IBLSApkRegistry.PubkeyRegistrationParams memory blsParams = IBLSApkRegistry.PubkeyRegistrationParams({ + pubkeyG1: operator.signingKey.publicKeyG1, + pubkeyG2: operator.signingKey.publicKeyG2, + pubkeyRegistrationSignature: signature + }); + + + registrationParamsData = abi.encode( + "test-socket", // Random socket string + blsParams + ); + + IAllocationManagerTypes.RegisterParams memory params = IAllocationManagerTypes.RegisterParams({ + avs: avs, + operatorSetIds: operatorSetIds, + data: registrationParamsData + }); + + // Register the operator in the Allocation Manager + allocationManagerInstance.registerForOperatorSets(operator.key.addr, params); + } + + function deregisterOperatorFromAVS_OpSet( + Operator memory operator, + address allocationManager, + address avs, + uint32[] calldata operatorSetIds + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + IAllocationManagerTypes.DeregisterParams memory params = IAllocationManagerTypes.DeregisterParams({ + operator: operator.key.addr, + avs: avs, + operatorSetIds: operatorSetIds + }); + + // Deregister the operator in the Allocation Manager + allocationManagerInstance.deregisterFromOperatorSets(params); + } + + function setAllocationDelay( + Operator memory operator, + address allocationManager, + uint32 delay + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + // Set the allocation delay for the operator + allocationManagerInstance.setAllocationDelay(operator.key.addr, delay); + } + + function modifyOperatorAllocations( + Operator memory operator, + address allocationManager, + IAllocationManagerTypes.AllocateParams[] memory params + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + allocationManagerInstance.modifyAllocations(operator.key.addr, params); + } + + function createAndAddOperator(uint256 salt) internal returns (Operator memory) { + Wallet memory operatorKey = + createWallet(salt); + BLSWallet memory signingKey = + createBLSWallet(salt); + + Operator memory newOperator = Operator({key: operatorKey, signingKey: signingKey}); + + return newOperator; + } + + function signMessage( + BLSWallet memory blsWallet, + bytes32 messageHash + ) internal view returns (BN254.G1Point memory) { + // Hash the message to a point on G1 + BN254.G1Point memory messagePoint = BN254.hashToG1(messageHash); + + // Sign by multiplying the hashed message point with the private key + return messagePoint.scalar_mul(blsWallet.privateKey); + } + + function signMessageWithOperator( + Operator memory operator, + bytes32 messageHash + ) internal view returns (BN254.G1Point memory) { + return signMessage(operator.signingKey, messageHash); + } +} \ No newline at end of file diff --git a/script/utils/UpgradeableProxyLib.sol b/script/utils/UpgradeableProxyLib.sol new file mode 100644 index 00000000..00fa4006 --- /dev/null +++ b/script/utils/UpgradeableProxyLib.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +// Deploy L2AVS proxy + +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; + +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; + +library UpgradeableProxyLib { + using stdJson for string; + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + bytes32 internal constant IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + bytes32 internal constant ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + function deployProxyAdmin() internal returns (address) { + return address(new ProxyAdmin()); + } + + function setUpEmptyProxy(address admin) internal returns (address) { + address emptyContract = address(new EmptyContract()); + return address(new TransparentUpgradeableProxy(emptyContract, admin, "")); + } + + function upgrade(address proxy, address implementation, bytes memory data) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), implementation, data); + } + + function upgrade(address proxy, address implementation) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), implementation); + } + + function upgradeAndCall(address proxy, address impl, bytes memory initData) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), impl, initData); + } + + function getAdmin(address proxy) internal view returns (address){ + bytes32 value = vm.load(proxy, ADMIN_SLOT); + return address(uint160(uint256(value))); + } + + function getImplementation(address proxy) internal view returns (address) { + bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT); + return address(uint160(uint256(value))); + } +} \ No newline at end of file diff --git a/script/utils/testdata/17000/core_testdata.json b/script/utils/testdata/17000/core_testdata.json new file mode 100644 index 00000000..2ebdc8fb --- /dev/null +++ b/script/utils/testdata/17000/core_testdata.json @@ -0,0 +1,23 @@ +{ + "addresses":{ + "avsDirectory": "0x141d6995556135D4997b2ff72EB443Be300353bC", + "avsDirectoryImplementation": "0x357978adC03375BD6a3605DE055fABb84695d79A", + "baseStrategyImplementation": "0x62450517EfA1CE60d79801daf8f95973865e8D40", + "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", + "delayedWithdrawalRouter": "0xC4BC46a87A67a531eCF7f74338E1FA79533334Fa", + "delayedWithdrawalRouterImplementation": "0x0011FA2c512063C495f77296Af8d195F33A8Dd38", + "delegationManager": "0x75dfE5B44C2E530568001400D3f704bC8AE350CC", + "delegationManagerImplementation": "0x56E88cb4f0136fC27D95499dE4BE2acf47946Fa1", + "eigenLayerPauserReg": "0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1", + "eigenLayerProxyAdmin": "0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B", + "eigenPodBeacon": "0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC", + "eigenPodImplementation": "0x2D6c7f9862BD80Cf0d9d93FC6b513D69E7Db7869", + "eigenPodManager": "0xB8d8952f572e67B11e43bC21250967772fa883Ff", + "eigenPodManagerImplementation": "0xc5B857A92245f64e9D90cCc5b096Db82eB77eB5c", + "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "rewardsCoordinator": "0xb22Ef643e1E067c994019A4C19e403253C05c2B0", + "rewardsCoordinatorImplementation": "0x76d4D84c90a2AFf213F7D859d2a288685A1a2Ede", + "slasher": "0x12699471dF8dca329C76D72823B1b79d55709384", + "slasherImplementation": "0x9460fCe11E1e0365419fa860599903B4E5097cf0" + } +} \ No newline at end of file diff --git a/script/utils/testdata/17000/middlware_testdata.json b/script/utils/testdata/17000/middlware_testdata.json new file mode 100644 index 00000000..6c25eeee --- /dev/null +++ b/script/utils/testdata/17000/middlware_testdata.json @@ -0,0 +1,18 @@ +{ + "addresses":{ + "blsApkRegistry": "0xAd7f9e558170a149Ca8E90f41Ab2444A5d3bd6aD", + "blsApkRegistryImplementation": "0x482a96D5879e32347d8df125f038D7eC8Ab358dd", + "eigenDAProxyAdmin": "0x9Fd7E279f5bD692Dc04792151E14Ad814FC60eC1", + "eigenDAServiceManager": "0x54A03db2784E3D0aCC08344D05385d0b62d4F432", + "eigenDAServiceManagerImplementation": "0xEB11a0f320E39d3371Fec4Bf5C76944DfBA8ee10", + "indexRegistry": "0x8cE5F2a53cBd29710eb94A04e40C07A4DdF15d10", + "indexRegistryImplementation": "0x1D4d6054BD11A5711ad7c5d3E376C987a603e17C", + "mockRollup": "0x0433646AdCeE95fbF89b3BFDb8157e75c19b6C2e", + "operatorStateRetriever": "0x17cA8C41a59466710443143b2ECF08CaA35d80ad", + "registryCoordinator": "0x2c61EA360D6500b58E7f481541A36B443Bc858c6", + "registryCoordinatorImplementation": "0x6f21A84E7f185cCBA248B436e3b583E609d1dE1D", + "serviceManagerRouter": "0xDb028E067fe81e9f406C2DE382Ba82e9cD7cBD03", + "stakeRegistry": "0x53668EBf2e28180e38B122c641BC51Ca81088871", + "stakeRegistryImplementation": "0x854dc9e5d011B060bf77B1a492302C349f2f00b5" + } +} \ No newline at end of file diff --git a/src/AVSRegistrar.sol b/src/AVSRegistrar.sol new file mode 100644 index 00000000..9a81e129 --- /dev/null +++ b/src/AVSRegistrar.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; + +abstract contract AVSRegistrar is IAVSRegistrar { + function registerOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) external virtual; + + function deregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) external virtual; +} diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 2bad724b..189716a6 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BLSApkRegistryStorage} from "./BLSApkRegistryStorage.sol"; @@ -79,7 +79,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { * @param quorumNumber The number of the new quorum */ function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); + require(apkHistory[quorumNumber].length == 0, QuorumAlreadyExists()); apkHistory[quorumNumber].push(ApkUpdate({ apkHash: bytes24(0), @@ -100,37 +100,29 @@ contract BLSApkRegistry is BLSApkRegistryStorage { BN254.G1Point calldata pubkeyRegistrationMessageHash ) external onlyRegistryCoordinator returns (bytes32 operatorId) { bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); - require( - pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); - require( - operatorToPubkeyHash[operator] == bytes32(0), - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); - require( - pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); + require(pubkeyHash != ZERO_PK_HASH, ZeroPubKey()); + require(operatorToPubkeyHash[operator] == bytes32(0), OperatorAlreadyRegistered()); + require(pubkeyHashToOperator[pubkeyHash] == address(0), BLSPubkeyAlreadyRegistered()); // gamma = h(sigma, P, P', H(m)) uint256 gamma = uint256(keccak256(abi.encodePacked( - params.pubkeyRegistrationSignature.X, - params.pubkeyRegistrationSignature.Y, - params.pubkeyG1.X, - params.pubkeyG1.Y, - params.pubkeyG2.X, - params.pubkeyG2.Y, - pubkeyRegistrationMessageHash.X, + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.pubkeyG2.Y, + pubkeyRegistrationMessageHash.X, pubkeyRegistrationMessageHash.Y ))) % BN254.FR_MODULUS; - - // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') + + // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') require(BN254.pairing( params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), BN254.negGeneratorG2(), pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), params.pubkeyG2 - ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + ), InvalidBLSSignatureOrPrivateKey()); operatorToPubkey[operator] = params.pubkeyG1; operatorToPubkeyHash[operator] = pubkeyHash; @@ -151,7 +143,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { // Validate quorum exists and get history length uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = apkHistory[quorumNumber].length; - require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); // Update aggregate public key for this quorum newApk = currentApk[quorumNumber].plus(point); @@ -185,11 +177,8 @@ contract BLSApkRegistry is BLSApkRegistryStorage { BN254.G1Point memory pubkey = operatorToPubkey[operator]; bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - require( - pubkeyHash != bytes32(0), - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); - + require(pubkeyHash != bytes32(0), OperatorNotRegistered()); + return (pubkey, pubkeyHash); } @@ -202,10 +191,10 @@ contract BLSApkRegistry is BLSApkRegistryStorage { uint256 blockNumber ) external view returns (uint32[] memory) { uint32[] memory indices = new uint32[](quorumNumbers.length); - + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - + uint256 quorumApkUpdatesLength = apkHistory[quorumNumber].length; if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { revert("BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update"); @@ -253,11 +242,11 @@ contract BLSApkRegistry is BLSApkRegistryStorage { */ require( blockNumber >= quorumApkUpdate.updateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" + BlockNumberTooRecent() ); require( quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" + BlockNumberNotLatest() ); return quorumApkUpdate.apkHash; @@ -280,9 +269,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { } function _checkRegistryCoordinator() internal view { - require( - msg.sender == address(registryCoordinator), - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinatorOwner()); } } diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 9597d5ac..b35b9362 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 5392289c..944ed4f7 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSSignatureChecker} from "./interfaces/IBLSSignatureChecker.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; @@ -31,10 +31,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { bool public staleStakesForbidden; modifier onlyCoordinatorOwner() { - require( - msg.sender == registryCoordinator.owner(), - "BLSSignatureChecker.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + require(msg.sender == registryCoordinator.owner(), OnlyRegistryCoordinatorOwner()); _; } @@ -91,29 +88,23 @@ contract BLSSignatureChecker is IBLSSignatureChecker { uint32 referenceBlockNumber, NonSignerStakesAndSignature memory params ) public view returns (QuorumStakeTotals memory, bytes32) { - require( - quorumNumbers.length != 0, - "BLSSignatureChecker.checkSignatures: empty quorum input" - ); + require(quorumNumbers.length != 0, InputEmptyQuorumNumbers()); require( (quorumNumbers.length == params.quorumApks.length) && (quorumNumbers.length == params.quorumApkIndices.length) && (quorumNumbers.length == params.totalStakeIndices.length) && (quorumNumbers.length == params.nonSignerStakeIndices.length), - "BLSSignatureChecker.checkSignatures: input quorum length mismatch" + InputArrayLengthMismatch() ); require( params.nonSignerPubkeys.length == params.nonSignerQuorumBitmapIndices.length, - "BLSSignatureChecker.checkSignatures: input nonsigner length mismatch" + InputNonSignerLengthMismatch() ); - require( - referenceBlockNumber < uint32(block.number), - "BLSSignatureChecker.checkSignatures: invalid reference block" - ); + require(referenceBlockNumber < uint32(block.number), InvalidReferenceBlocknumber()); // This method needs to calculate the aggregate pubkey for all signing operators across // all signing quorums. To do that, we can query the aggregate pubkey for each quorum @@ -155,7 +146,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { require( uint256(nonSigners.pubkeyHashes[j]) > uint256(nonSigners.pubkeyHashes[j - 1]), - "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted" + NonSignerPubkeysNotSorted() ); } @@ -207,7 +198,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { ) + withdrawalDelayBlocks > referenceBlockNumber, - "BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window" + StaleStakesForbidden() ); } @@ -220,7 +211,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { blockNumber: referenceBlockNumber, index: params.quorumApkIndices[i] }), - "BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk" + InvalidQuorumApkHash() ); apk = apk.plus(params.quorumApks[i]); @@ -274,14 +265,8 @@ contract BLSSignatureChecker is IBLSSignatureChecker { params.apkG2, params.sigma ); - require( - pairingSuccessful, - "BLSSignatureChecker.checkSignatures: pairing precompile call failed" - ); - require( - signatureIsValid, - "BLSSignatureChecker.checkSignatures: signature is invalid" - ); + require(pairingSuccessful, InvalidBLSPairingKey()); + require(signatureIsValid, InvalidBLSSignature()); } // set signatoryRecordHash variable used for fraudproofs bytes32 signatoryRecordHash = keccak256( diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index 3a164f57..8ebf6002 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {IEjectionManager} from "./interfaces/IEjectionManager.sol"; @@ -10,10 +10,13 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; * @title Used for automated ejection of operators from the RegistryCoordinator under a ratelimit * @author Layr Labs, Inc. */ -contract EjectionManager is IEjectionManager, OwnableUpgradeable{ +contract EjectionManager is IEjectionManager, OwnableUpgradeable { /// @notice The basis point denominator for the ejectable stake percent - uint16 internal constant BIPS_DENOMINATOR = 10000; + uint16 internal constant BIPS_DENOMINATOR = 10_000; + + /// @notice The max number of quorums + uint8 internal constant MAX_QUORUM_COUNT = 192; /// @notice the RegistryCoordinator contract that is the entry point for ejection IRegistryCoordinator public immutable registryCoordinator; @@ -64,7 +67,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ * @dev The owner can eject operators without recording of stake ejection */ function ejectOperators(bytes32[][] memory _operatorIds) external { - require(isEjector[msg.sender] || msg.sender == owner(), "Ejector: Only owner or ejector can eject"); + require(isEjector[msg.sender] || msg.sender == owner(), OnlyOwnerOrEjector()); for(uint i = 0; i < _operatorIds.length; ++i) { uint8 quorumNumber = uint8(i); @@ -98,7 +101,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ registryCoordinator.getOperatorFromId(_operatorIds[i][j]), abi.encodePacked(quorumNumber) ); - + emit OperatorEjected(_operatorIds[i][j], quorumNumber); } @@ -134,6 +137,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ ///@dev internal function to set the quorum ejection params function _setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) internal { + require(_quorumNumber < MAX_QUORUM_COUNT, MaxQuorumCount()); quorumEjectionParams[_quorumNumber] = _quorumEjectionParams; emit QuorumEjectionParamsSet(_quorumNumber, _quorumEjectionParams.rateLimitWindow, _quorumEjectionParams.ejectableStakePercent); } @@ -149,25 +153,27 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ * @param _quorumNumber The quorum number to view ejectable stake for */ function amountEjectableForQuorum(uint8 _quorumNumber) public view returns (uint256) { - uint256 cutoffTime = block.timestamp - quorumEjectionParams[_quorumNumber].rateLimitWindow; - uint256 totalEjectable = uint256(quorumEjectionParams[_quorumNumber].ejectableStakePercent) * uint256(stakeRegistry.getCurrentTotalStake(_quorumNumber)) / uint256(BIPS_DENOMINATOR); - uint256 totalEjected; - uint256 i; + uint256 totalEjectable = uint256(quorumEjectionParams[_quorumNumber].ejectableStakePercent) + * uint256(stakeRegistry.getCurrentTotalStake(_quorumNumber)) / uint256(BIPS_DENOMINATOR); + if (stakeEjectedForQuorum[_quorumNumber].length == 0) { return totalEjectable; } - i = stakeEjectedForQuorum[_quorumNumber].length - 1; - while(stakeEjectedForQuorum[_quorumNumber][i].timestamp > cutoffTime) { + uint256 cutoffTime = block.timestamp - quorumEjectionParams[_quorumNumber].rateLimitWindow; + uint256 totalEjected = 0; + uint256 i = stakeEjectedForQuorum[_quorumNumber].length - 1; + + while (stakeEjectedForQuorum[_quorumNumber][i].timestamp > cutoffTime) { totalEjected += stakeEjectedForQuorum[_quorumNumber][i].stakeEjected; - if(i == 0){ + if (i == 0) { break; } else { --i; } } - if(totalEjected >= totalEjectable){ + if (totalEjected >= totalEjectable) { return 0; } return totalEjectable - totalEjected; diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 8df2c0a1..cab62a17 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IndexRegistryStorage} from "./IndexRegistryStorage.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; @@ -38,7 +38,7 @@ contract IndexRegistry is IndexRegistryStorage { * 4) the operator is not already registered */ function registerOperator( - bytes32 operatorId, + bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator returns(uint32[] memory) { uint32[] memory numOperatorsPerQuorum = new uint32[](quorumNumbers.length); @@ -47,7 +47,7 @@ contract IndexRegistry is IndexRegistryStorage { // Validate quorum exists and get current operator count uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = _operatorCountHistory[quorumNumber].length; - require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); /** * Increase the number of operators currently active for this quorum, @@ -80,14 +80,14 @@ contract IndexRegistry is IndexRegistryStorage { * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for */ function deregisterOperator( - bytes32 operatorId, + bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { for (uint256 i = 0; i < quorumNumbers.length; i++) { // Validate quorum exists and get the operatorIndex of the operator being deregistered uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = _operatorCountHistory[quorumNumber].length; - require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); uint32 operatorIndexToRemove = currentOperatorIndex[quorumNumber][operatorId]; /** @@ -113,7 +113,7 @@ contract IndexRegistry is IndexRegistryStorage { * @param quorumNumber The number of the new quorum */ function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(_operatorCountHistory[quorumNumber].length == 0, "IndexRegistry.createQuorum: quorum already exists"); + require(_operatorCountHistory[quorumNumber].length == 0, QuorumDoesNotExist()); _operatorCountHistory[quorumNumber].push(QuorumUpdate({ numOperators: 0, @@ -131,7 +131,7 @@ contract IndexRegistry is IndexRegistryStorage { function _increaseOperatorCount(uint8 quorumNumber) internal returns (uint32) { QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); uint32 newOperatorCount = lastUpdate.numOperators + 1; - + _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); // If this is the first time we're using this operatorIndex, push its first update @@ -152,9 +152,9 @@ contract IndexRegistry is IndexRegistryStorage { function _decreaseOperatorCount(uint8 quorumNumber) internal returns (uint32) { QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); uint32 newOperatorCount = lastUpdate.numOperators - 1; - + _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); - + return newOperatorCount; } @@ -198,7 +198,7 @@ contract IndexRegistry is IndexRegistryStorage { * @param operatorId operatorId of the operator to update * @param quorumNumber quorumNumber of the operator to update * @param operatorIndex the latest index of that operator in the list of operators registered for this quorum - */ + */ function _assignOperatorToIndex(bytes32 operatorId, uint8 quorumNumber, uint32 operatorIndex) internal { OperatorUpdate storage lastUpdate = _latestOperatorIndexUpdate(quorumNumber, operatorIndex); @@ -249,7 +249,7 @@ contract IndexRegistry is IndexRegistryStorage { * @dev Reverts if the quorum does not exist, or if the blockNumber is from before the quorum existed */ function _operatorCountAtBlockNumber( - uint8 quorumNumber, + uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32){ uint256 historyLength = _operatorCountHistory[quorumNumber].length; @@ -262,17 +262,17 @@ contract IndexRegistry is IndexRegistryStorage { return quorumUpdate.numOperators; } } - + revert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); } - + /** * @return operatorId at the given `operatorIndex` at the given `blockNumber` for the given `quorumNumber` * Precondition: requires that the operatorIndex was used active at the given block number for quorum */ function _operatorIdForIndexAtBlockNumber( - uint8 quorumNumber, - uint32 operatorIndex, + uint8 quorumNumber, + uint32 operatorIndex, uint32 blockNumber ) internal view returns(bytes32) { uint256 historyLength = _operatorIndexHistory[quorumNumber][operatorIndex].length; @@ -320,7 +320,7 @@ contract IndexRegistry is IndexRegistryStorage { /// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber` function getOperatorListAtBlockNumber( - uint8 quorumNumber, + uint8 quorumNumber, uint32 blockNumber ) external view returns (bytes32[] memory){ uint32 operatorCount = _operatorCountAtBlockNumber(quorumNumber, blockNumber); @@ -328,8 +328,8 @@ contract IndexRegistry is IndexRegistryStorage { for (uint256 i = 0; i < operatorCount; i++) { operatorList[i] = _operatorIdForIndexAtBlockNumber(quorumNumber, uint32(i), blockNumber); require( - operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, - "IndexRegistry.getOperatorListAtBlockNumber: operator does not exist at the given block number" + operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, + OperatorIdDoesNotExist() ); } return operatorList; @@ -342,6 +342,6 @@ contract IndexRegistry is IndexRegistryStorage { } function _checkRegistryCoordinator() internal view { - require(msg.sender == address(registryCoordinator), "IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator()); } } diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index b28a4510..b5b800d6 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -22,7 +22,7 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { /// @notice maps quorumNumber => operator id => current operatorIndex /// NOTE: This mapping is NOT updated when an operator is deregistered, /// so it's possible that an index retrieved from this mapping is inaccurate. - /// If you're querying for an operator that might be deregistered, ALWAYS + /// If you're querying for an operator that might be deregistered, ALWAYS /// check this index against the latest `_operatorIndexHistory` entry mapping(uint8 => mapping(bytes32 => uint32)) public currentOperatorIndex; /// @notice maps quorumNumber => operatorIndex => historical operator ids at that index diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index f0547a2d..87672b66 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; @@ -22,30 +22,32 @@ contract OperatorStateRetriever { struct CheckSignaturesIndices { uint32[] nonSignerQuorumBitmapIndices; uint32[] quorumApkIndices; - uint32[] totalStakeIndices; + uint32[] totalStakeIndices; uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] } + error OperatorNotRegistered(); + /** * @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.) - * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, + * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, * operators don't need to run indexers to fetch the data. * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from - * @param operatorId the id of the operator to fetch the quorums lists + * @param operatorId the id of the operator to fetch the quorums lists * @param blockNumber is the block number to get the operator state for * @return 1) the quorumBitmap of the operator at the given blockNumber - * 2) 2d array of Operator structs. For each quorum the provided operator + * 2) 2d array of Operator structs. For each quorum the provided operator * was a part of at `blockNumber`, an ordered list of operators. */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes32 operatorId, + IRegistryCoordinator registryCoordinator, + bytes32 operatorId, uint32 blockNumber ) external view returns (uint256, Operator[][] memory) { bytes32[] memory operatorIds = new bytes32[](1); operatorIds[0] = operatorId; uint256 index = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0]; - + uint256 quorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); @@ -54,7 +56,7 @@ contract OperatorStateRetriever { } /** - * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator + * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator * may call this function directly to get the operator state for a given block number * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param quorumNumbers are the ids of the quorums to get the operator state for @@ -62,8 +64,8 @@ contract OperatorStateRetriever { * @return 2d array of Operators. For each quorum, an ordered list of Operators */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes memory quorumNumbers, + IRegistryCoordinator registryCoordinator, + bytes memory quorumNumbers, uint32 blockNumber ) public view returns(Operator[][] memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); @@ -83,28 +85,28 @@ contract OperatorStateRetriever { }); } } - + return operators; } /** * @notice this is called by the AVS operator to get the relevant indices for the checkSignatures function - * if they are not running an indexer + * if they are not running an indexer * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param referenceBlockNumber is the block number to get the indices for * @param quorumNumbers are the ids of the quorums to get the operator state for * @param nonSignerOperatorIds are the ids of the nonsigning operators * @return 1) the indices of the quorumBitmaps for each of the operators in the @param nonSignerOperatorIds array at the given blocknumber * 2) the indices of the total stakes entries for the given quorums at the given blocknumber - * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a + * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a * part of (for each nonsigner, an array of length the number of quorums they were a part of * that are also part of the provided quorumNumbers) at the given blocknumber * 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber */ function getCheckSignaturesIndices( IRegistryCoordinator registryCoordinator, - uint32 referenceBlockNumber, - bytes calldata quorumNumbers, + uint32 referenceBlockNumber, + bytes calldata quorumNumbers, bytes32[] calldata nonSignerOperatorIds ) external view returns (CheckSignaturesIndices memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); @@ -115,7 +117,7 @@ contract OperatorStateRetriever { // get the indices of the totalStake updates for each of the quorums in the quorumNumbers array checkSignaturesIndices.totalStakeIndices = stakeRegistry.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers); - + checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length); for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) { uint256 numNonSignersForQuorum = 0; @@ -124,15 +126,15 @@ contract OperatorStateRetriever { for (uint i = 0; i < nonSignerOperatorIds.length; i++) { // get the quorumBitmap for the operator at the given blocknumber and index - uint192 nonSignerQuorumBitmap = + uint192 nonSignerQuorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( - nonSignerOperatorIds[i], - referenceBlockNumber, + nonSignerOperatorIds[i], + referenceBlockNumber, checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] ); - - require(nonSignerQuorumBitmap != 0, "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber"); - + + require(nonSignerQuorumBitmap != 0, OperatorNotRegistered()); + // if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) { // get the index of the stake update for the operator at the given blocknumber and quorum number @@ -210,5 +212,5 @@ contract OperatorStateRetriever { operators[i] = registryCoordinator.getOperatorFromId(operatorIds[i]); } } - + } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 6d2ff3f4..c155bfb5 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -1,18 +1,23 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IStrategy } from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import { IAllocationManager, OperatorSet, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {BN254} from "./libraries/BN254.sol"; +import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; +import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol"; +import {AVSRegistrar} from "./AVSRegistrar.sol"; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -20,28 +25,30 @@ import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.so import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; /** * @title A `RegistryCoordinator` that has three registries: * 1) a `StakeRegistry` that keeps track of operators' stakes * 2) a `BLSApkRegistry` that keeps track of operators' BLS public keys and aggregate BLS public keys for each quorum * 3) an `IndexRegistry` that keeps track of an ordered list of operators for each quorum - * + * * @author Layr Labs, Inc. */ -contract RegistryCoordinator is - EIP712, - Initializable, +contract RegistryCoordinator is + EIP712, + Initializable, Pausable, OwnableUpgradeable, - RegistryCoordinatorStorage, - ISocketUpdater, + RegistryCoordinatorStorage, + AVSRegistrar, + ISocketUpdater, ISignatureUtils { using BitmapUtils for *; using BN254 for BN254.G1Point; - modifier onlyEjector { + modifier onlyEjector() { _checkEjector(); _; } @@ -57,10 +64,12 @@ contract RegistryCoordinator is IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry - ) + IIndexRegistry _indexRegistry, + IPauserRegistry _pauserRegistry + ) RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) - EIP712("AVSRegistryCoordinator", "v0.0.1") + EIP712("AVSRegistryCoordinator", "v0.0.1") + Pausable(_pauserRegistry) { _disableInitializers(); } @@ -69,7 +78,6 @@ contract RegistryCoordinator is * @param _initialOwner will hold the owner role * @param _churnApprover will hold the churnApprover role, which authorizes registering with churn * @param _ejector will hold the ejector role, which can force-eject operators from quorums - * @param _pauserRegistry a registry of addresses that can pause the contract * @param _initialPausedStatus pause status after calling initialize * Config for initial quorums (see `createQuorum`): * @param _operatorSetParams max operator count and operator churn parameters @@ -80,21 +88,25 @@ contract RegistryCoordinator is address _initialOwner, address _churnApprover, address _ejector, - IPauserRegistry _pauserRegistry, uint256 _initialPausedStatus, OperatorSetParam[] memory _operatorSetParams, uint96[] memory _minimumStakes, - IStakeRegistry.StrategyParams[][] memory _strategyParams + IStakeRegistry.StrategyParams[][] memory _strategyParams, + StakeType[] memory _stakeTypes, + uint32[] memory _lookAheadPeriods ) external initializer { require( - _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, - "RegistryCoordinator.initialize: input length mismatch" + _operatorSetParams.length == _minimumStakes.length + && _minimumStakes.length == _strategyParams.length + && _strategyParams.length == _stakeTypes.length + && _stakeTypes.length == _lookAheadPeriods.length, + InputLengthMismatch() ); - + // Initialize roles _transferOwnership(_initialOwner); - _initializePauser(_pauserRegistry, _initialPausedStatus); _setChurnApprover(_churnApprover); + _setPausedStatus(_initialPausedStatus); _setEjector(_ejector); // Add registry contracts to the registries array @@ -104,13 +116,15 @@ contract RegistryCoordinator is // Create quorums for (uint256 i = 0; i < _operatorSetParams.length; i++) { - _createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i]); + _createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i], _stakeTypes[i], _lookAheadPeriods[i]); } } - /******************************************************************************* - EXTERNAL FUNCTIONS - *******************************************************************************/ + /** + * + * EXTERNAL FUNCTIONS + * + */ /** * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum @@ -123,11 +137,12 @@ contract RegistryCoordinator is * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED */ function registerOperator( - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(!isUsingOperatorSets(), OperatorSetsEnabled()); /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -140,9 +155,9 @@ contract RegistryCoordinator is // Register the operator in each of the registry contracts and update the operator's // quorum bitmap and registration status uint32[] memory numOperatorsPerQuorum = _registerOperator({ - operator: msg.sender, + operator: msg.sender, operatorId: operatorId, - quorumNumbers: quorumNumbers, + quorumNumbers: quorumNumbers, socket: socket, operatorSignature: operatorSignature }).numOperatorsPerQuorum; @@ -154,7 +169,7 @@ contract RegistryCoordinator is require( numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, - "RegistryCoordinator.registerOperator: operator count exceeds maximum" + MaxQuorumsReached() ); } } @@ -172,15 +187,19 @@ contract RegistryCoordinator is * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED */ function registerOperatorWithChurn( - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - OperatorKickParam[] calldata operatorKickParams, + bytes calldata quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + OperatorKickParam[] memory operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); - + require(!isUsingOperatorSets(), OperatorSetsEnabled()); + require( + operatorKickParams.length == quorumNumbers.length, + InputLengthMismatch() + ); + /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -212,7 +231,7 @@ contract RegistryCoordinator is // is exceeded, use `operatorKickParams` to deregister an existing operator to make space for (uint256 i = 0; i < quorumNumbers.length; i++) { OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; - + /** * If the new operator count for any quorum exceeds the maximum, validate * that churn can be performed, then deregister the specified operator @@ -227,7 +246,7 @@ contract RegistryCoordinator is setParams: operatorSetParams }); - _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); + _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i + 1]); } } } @@ -236,22 +255,106 @@ contract RegistryCoordinator is * @notice Deregisters the caller from one or more quorums * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from */ - function deregisterOperator( - bytes calldata quorumNumbers - ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { - _deregisterOperator({ - operator: msg.sender, - quorumNumbers: quorumNumbers + function deregisterOperator(bytes memory quorumNumbers) + external + onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) + { + // Check that either: + // 1. The AVS hasn't migrated to operator sets yet (!isOperatorSetAVS), or + // 2. The AVS has migrated but this is an M2 quorum + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + require( + !isOperatorSetAVS || isM2Quorum[quorumNumber], + OperatorSetsEnabled() + ); + } + _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); + } + + function isUsingOperatorSets() public view returns (bool) { + return isOperatorSetAVS; + } + + function enableOperatorSets() external onlyOwner { + require(!isOperatorSetAVS, OperatorSetsEnabled()); + /// Triggers the updates to use operator sets ie setsAVSRegistrar + /// Opens up the AVS Registrar Hooks on this contract to be callable by the ALM + /// Allows creation of quorums with slashable and total delegated stake for operator sets + /// Sets all quorums created before this call as m2 quorums in a mapping so that we can gate function calls to deregister + /// M2 Registrations turn off once migrated. M2 deregistration remain open for only m2 quorums + // Set this contract as the AVS registrar in the service manager + serviceManager.setAVSRegistrar(IAVSRegistrar(address(this))); + + // Set all existing quorums as m2 quorums + for (uint8 i = 0; i < quorumCount; i++) { + isM2Quorum[i] = true; + } + + // Enable operator sets mode + isOperatorSetAVS = true; + } + + function registerOperator( + address operator, + uint32[] memory operatorSetIds, + bytes memory data + ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); + } + _checkAllocationManager(); + + // Decode registration data from bytes + ( + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params + ) = abi.decode(data, (string, IBLSApkRegistry.PubkeyRegistrationParams)); + + // Get operator ID from BLS registry + bytes32 operatorId = _getOrCreateOperatorId(operator, params); + bytes memory quorumNumbers = new bytes(operatorSetIds.length); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); + } + + // Register operator with decoded parameters + _registerOperatorToOperatorSet({ + operator: operator, + operatorId: operatorId, + quorumNumbers: quorumNumbers, + socket: socket }); } + function deregisterOperator( + address operator, + uint32[] memory operatorSetIds + ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); + } + _checkAllocationManager(); + bytes memory quorumNumbers = new bytes(operatorSetIds.length); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); + } + + _deregisterOperator(operator, quorumNumbers); + } + /** * @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator * is found to be below the minimum stake for the quorum, they are deregistered. * @dev stakes are queried from the Eigenlayer core DelegationManager contract * @param operators a list of operator addresses to update */ - function updateOperators(address[] calldata operators) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { + function updateOperators(address[] memory operators) + external + onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) + { for (uint256 i = 0; i < operators.length; i++) { address operator = operators[i]; OperatorInfo memory operatorInfo = _operatorInfo[operator]; @@ -275,21 +378,22 @@ contract RegistryCoordinator is * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` * for the corresponding quorum. - * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to + * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to * this method is broadcast (but before it is executed), the method will fail */ function updateOperatorsForQuorum( - address[][] calldata operatorsPerQuorum, + address[][] memory operatorsPerQuorum, bytes calldata quorumNumbers ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { - // Input validation + // Input validation // - all quorums should exist (checked against `quorumCount` in orderedBytesArrayToBitmap) // - there should be no duplicates in `quorumNumbers` // - there should be one list of operators per quorum - uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumBitmap = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); require( operatorsPerQuorum.length == quorumNumbers.length, - "RegistryCoordinator.updateOperatorsForQuorum: input length mismatch" + InputLengthMismatch() ); // For each quorum, update ALL registered operators @@ -297,10 +401,10 @@ contract RegistryCoordinator is uint8 quorumNumber = uint8(quorumNumbers[i]); // Ensure we've passed in the correct number of operators for this quorum - address[] calldata currQuorumOperators = operatorsPerQuorum[i]; + address[] memory currQuorumOperators = operatorsPerQuorum[i]; require( currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total" + QuorumOperatorCountMismatch() ); address prevOperatorAddress = address(0); @@ -310,26 +414,26 @@ contract RegistryCoordinator is // ... then, update their stakes for (uint256 j = 0; j < currQuorumOperators.length; ++j) { address operator = currQuorumOperators[j]; - + OperatorInfo memory operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - + { uint192 currentBitmap = _currentOperatorBitmap(operatorId); // Check that the operator is registered require( BitmapUtils.isSet(currentBitmap, quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum" + NotRegisteredForQuorum() ); // Prevent duplicate operators require( operator > prevOperatorAddress, - "RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order" + NotSorted() ); } - + // Update the operator - _updateOperator(operator, operatorInfo, quorumNumbers[i:i+1]); + _updateOperator(operator, operatorInfo, quorumNumbers[i:i + 1]); prevOperatorAddress = operator; } @@ -344,13 +448,18 @@ contract RegistryCoordinator is * @param socket is the new socket of the operator */ function updateSocket(string memory socket) external { - require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator is not registered"); + require( + _operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, + NotRegistered() + ); emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } - /******************************************************************************* - EXTERNAL FUNCTIONS - EJECTOR - *******************************************************************************/ + /** + * + * EXTERNAL FUNCTIONS - EJECTOR + * + */ /** * @notice Forcibly deregisters an operator from one or more quorums @@ -358,31 +467,27 @@ contract RegistryCoordinator is * @param quorumNumbers the quorum numbers to eject the operator from * @dev possible race condition if prior to being ejected for a set of quorums the operator self deregisters from a subset */ - function ejectOperator( - address operator, - bytes calldata quorumNumbers - ) external onlyEjector { + function ejectOperator(address operator, bytes memory quorumNumbers) external onlyEjector { lastEjectionTimestamp[operator] = block.timestamp; OperatorInfo storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - if( - operatorInfo.status == OperatorStatus.REGISTERED && - !quorumsToRemove.isEmpty() && - quorumsToRemove.isSubsetOf(currentBitmap) - ){ - _deregisterOperator({ - operator: operator, - quorumNumbers: quorumNumbers - }); + if ( + operatorInfo.status == OperatorStatus.REGISTERED && !quorumsToRemove.isEmpty() + && quorumsToRemove.isSubsetOf(currentBitmap) + ) { + _deregisterOperator({operator: operator, quorumNumbers: quorumNumbers}); } } - /******************************************************************************* - EXTERNAL FUNCTIONS - OWNER - *******************************************************************************/ + /** + * + * EXTERNAL FUNCTIONS - OWNER + * + */ /** * @notice Creates a quorum and initializes it in each registry contract @@ -391,13 +496,26 @@ contract RegistryCoordinator is * registered * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to * calculate an operator's stake weight for the quorum + * @dev For m2 AVS this function has the same behavior as createQuorum before + * For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set + * */ - function createQuorum( + function createTotalDelegatedStakeQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) external virtual onlyOwner { - _createQuorum(operatorSetParams, minimumStake, strategyParams); + _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_DELEGATED, 0); + } + + function createSlashableStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistry.StrategyParams[] memory strategyParams, + uint32 lookAheadPeriod + ) external virtual onlyOwner { + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); + _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_SLASHABLE, lookAheadPeriod); } /** @@ -408,7 +526,7 @@ contract RegistryCoordinator is * @dev only callable by the owner */ function setOperatorSetParams( - uint8 quorumNumber, + uint8 quorumNumber, OperatorSetParam memory operatorSetParams ) external onlyOwner quorumExists(quorumNumber) { _setOperatorSetParams(quorumNumber, operatorSetParams); @@ -434,7 +552,7 @@ contract RegistryCoordinator is } /** - * @notice Sets the ejection cooldown, which is the time an operator must wait in + * @notice Sets the ejection cooldown, which is the time an operator must wait in * seconds afer ejection before registering for any quorum * @param _ejectionCooldown the new ejection cooldown in seconds * @dev only callable by the owner @@ -443,24 +561,25 @@ contract RegistryCoordinator is ejectionCooldown = _ejectionCooldown; } - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - + /** + * + * INTERNAL FUNCTIONS + * + */ struct RegisterResults { uint32[] numOperatorsPerQuorum; uint96[] operatorStakes; uint96[] totalStakes; } - /** + /** * @notice Register the operator for one or more quorums. This method updates the * operator's quorum bitmap, socket, and status, then registers them with each registry. */ function _registerOperator( - address operator, + address operator, bytes32 operatorId, - bytes calldata quorumNumbers, + bytes memory quorumNumbers, string memory socket, SignatureWithSaltAndExpiry memory operatorSignature ) internal virtual returns (RegisterResults memory results) { @@ -471,43 +590,104 @@ contract RegistryCoordinator is * - the operator is not currently registered for any quorums we're registering for * Then, calculate the operator's new bitmap after registration */ - uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumsToAdd = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0"); - require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); + require( + !quorumsToAdd.isEmpty(), BitmapEmpty() + ); + require( + quorumsToAdd.noBitsInCommon(currentBitmap), + AlreadyRegisteredForQuorums() + ); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); // Check that the operator can reregister if ejected - require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "RegistryCoordinator._registerOperator: operator cannot reregister yet"); + require( + lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, + CannotReregisterYet() + ); /** * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: * if we're `REGISTERED`, the operatorId and status are already correct. */ - _updateOperatorBitmap({ - operatorId: operatorId, - newBitmap: newBitmap - }); + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); emit OperatorSocketUpdate(operatorId, socket); // If the operator wasn't registered for any quorums, update their status // and register them with this AVS in EigenLayer core (DelegationManager) if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { - _operatorInfo[operator] = OperatorInfo({ - operatorId: operatorId, - status: OperatorStatus.REGISTERED - }); + _operatorInfo[operator] = + OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); - // Register the operator with the EigenLayer core contracts via this AVS's ServiceManager serviceManager.registerOperatorToAVS(operator, operatorSignature); - emit OperatorRegistered(operator, operatorId); + + } + + // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry + blsApkRegistry.registerOperator(operator, quorumNumbers); + (results.operatorStakes, results.totalStakes) = + stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); + results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + + return results; + } + + /** + * @notice Register the operator for one or more quorums. This method updates the + * operator's quorum bitmap, socket, and status, then registers them with each registry. + */ + function _registerOperatorToOperatorSet( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + string memory socket + ) internal virtual returns (RegisterResults memory results) { + /** + * Get bitmap of quorums to register for and operator's current bitmap. Validate that: + * - we're trying to register for at least 1 quorum + * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is not currently registered for any quorums we're registering for + * Then, calculate the operator's new bitmap after registration + */ + uint192 quorumsToAdd = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + require( + !quorumsToAdd.isEmpty(), BitmapEmpty() + ); + require( + quorumsToAdd.noBitsInCommon(currentBitmap), + AlreadyRegisteredForQuorums() + ); + uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); + + // Check that the operator can reregister if ejected + require( + lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, + CannotReregisterYet() + ); + + /** + * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: + * if we're `REGISTERED`, the operatorId and status are already correct. + */ + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + emit OperatorSocketUpdate(operatorId, socket); + + // If the operator wasn't registered for any quorums, update their status + // and register them with this AVS in EigenLayer core (DelegationManager) + if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { + _operatorInfo[operator] = OperatorInfo(operatorId, OperatorStatus.REGISTERED); } // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry blsApkRegistry.registerOperator(operator, quorumNumbers); - (results.operatorStakes, results.totalStakes) = + (results.operatorStakes, results.totalStakes) = stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); @@ -519,7 +699,12 @@ contract RegistryCoordinator is * @dev Reverts if the caller is not the ejector */ function _checkEjector() internal view { - require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector"); + require(msg.sender == ejector, OnlyEjector()); + } + + function _checkAllocationManager() internal view { + address allocationManager = address(serviceManager.allocationManager()); + require(msg.sender == allocationManager, OnlyAllocationManager()); } /** @@ -529,8 +714,8 @@ contract RegistryCoordinator is */ function _checkQuorumExists(uint8 quorumNumber) internal view { require( - quorumNumber < quorumCount, - "RegistryCoordinator.quorumExists: quorum does not exist" + quorumNumber < quorumCount, + QuorumDoesNotExist() ); } @@ -544,11 +729,13 @@ contract RegistryCoordinator is */ function _getOrCreateOperatorId( address operator, - IBLSApkRegistry.PubkeyRegistrationParams calldata params + IBLSApkRegistry.PubkeyRegistrationParams memory params ) internal returns (bytes32 operatorId) { operatorId = blsApkRegistry.getOperatorId(operator); if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(operator, params, pubkeyRegistrationMessageHash(operator)); + operatorId = blsApkRegistry.registerBLSPublicKey( + operator, params, pubkeyRegistrationMessageHash(operator) + ); } return operatorId; } @@ -572,27 +759,33 @@ contract RegistryCoordinator is * mentioned above */ function _validateChurn( - uint8 quorumNumber, + uint8 quorumNumber, uint96 totalQuorumStake, - address newOperator, + address newOperator, uint96 newOperatorStake, - OperatorKickParam memory kickParams, + OperatorKickParam memory kickParams, OperatorSetParam memory setParams ) internal view { address operatorToKick = kickParams.operator; bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; - require(newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self"); - require(kickParams.quorumNumber == quorumNumber, "RegistryCoordinator._validateChurn: quorumNumber not the same as signed"); + require( + newOperator != operatorToKick, + CannotChurnSelf() + ); + require( + kickParams.quorumNumber == quorumNumber, + QuorumOperatorCountMismatch() + ); // Get the target operator's stake and check that it is below the kick thresholds uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); require( newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), - "RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn" + InsufficientStakeForChurn() ); require( operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), - "RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" + CannotKickOperatorAboveThreshold() ); } @@ -601,15 +794,15 @@ contract RegistryCoordinator is * This method updates the operator's quorum bitmap and status, then deregisters * the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry */ - function _deregisterOperator( - address operator, - bytes memory quorumNumbers - ) internal virtual { + function _deregisterOperator(address operator, bytes memory quorumNumbers) internal virtual { // Fetch the operator's info and ensure they are registered OperatorInfo storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - require(operatorInfo.status == OperatorStatus.REGISTERED, "RegistryCoordinator._deregisterOperator: operator is not registered"); - + require( + operatorInfo.status == OperatorStatus.REGISTERED, + NotRegistered() + ); + /** * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: * - we're trying to deregister from at least 1 quorum @@ -617,19 +810,23 @@ contract RegistryCoordinator is * - the operator is currently registered for any quorums we're trying to deregister from * Then, calculate the operator's new bitmap after deregistration */ - uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToRemove.isEmpty(), "RegistryCoordinator._deregisterOperator: bitmap cannot be 0"); - require(quorumsToRemove.isSubsetOf(currentBitmap), "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); + require( + !quorumsToRemove.isEmpty(), + BitmapCannotBeZero() + ); + require( + quorumsToRemove.isSubsetOf(currentBitmap), + NotRegisteredForQuorum() + ); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); // Update operator's bitmap and status - _updateOperatorBitmap({ - operatorId: operatorId, - newBitmap: newBitmap - }); + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - // If the operator is no longer registered for any quorums, update their status and deregister + // If the operator is no longer registered for any quorums, update their status and deregister // them from the AVS via the EigenLayer core contracts if (newBitmap.isEmpty()) { operatorInfo.status = OperatorStatus.DEREGISTERED; @@ -658,13 +855,14 @@ contract RegistryCoordinator is return; } bytes32 operatorId = operatorInfo.operatorId; - uint192 quorumsToRemove = stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate); + uint192 quorumsToRemove = + stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate); if (!quorumsToRemove.isEmpty()) { _deregisterOperator({ operator: operator, quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove) - }); + }); } } @@ -672,7 +870,10 @@ contract RegistryCoordinator is * @notice Returns the stake threshold required for an incoming operator to replace an existing operator * The incoming operator must have more stake than the return value. */ - function _individualKickThreshold(uint96 operatorStake, OperatorSetParam memory setParams) internal pure returns (uint96) { + function _individualKickThreshold( + uint96 operatorStake, + OperatorSetParam memory setParams + ) internal pure returns (uint96) { return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; } @@ -680,28 +881,43 @@ contract RegistryCoordinator is * @notice Returns the total stake threshold required for an operator to remain in a quorum. * The operator must have at least the returned stake amount to keep their position. */ - function _totalKickThreshold(uint96 totalStake, OperatorSetParam memory setParams) internal pure returns (uint96) { + function _totalKickThreshold( + uint96 totalStake, + OperatorSetParam memory setParams + ) internal pure returns (uint96) { return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; } /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce function _verifyChurnApproverSignature( address registeringOperator, - bytes32 registeringOperatorId, - OperatorKickParam[] memory operatorKickParams, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature ) internal { // make sure the salt hasn't been used already - require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used"); - require(churnApproverSignature.expiry >= block.timestamp, "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); + require( + !isChurnApproverSaltUsed[churnApproverSignature.salt], + ChurnApproverSaltUsed() + ); + require( + churnApproverSignature.expiry >= block.timestamp, + SignatureExpired() + ); // set salt used to true - isChurnApproverSaltUsed[churnApproverSignature.salt] = true; - - // check the churnApprover's signature - EIP1271SignatureUtils.checkSignature_EIP1271( - churnApprover, - calculateOperatorChurnApprovalDigestHash(registeringOperator, registeringOperatorId, operatorKickParams, churnApproverSignature.salt, churnApproverSignature.expiry), + isChurnApproverSaltUsed[churnApproverSignature.salt] = true; + + // check the churnApprover's signature + SignatureCheckerLib.isValidSignature( + churnApprover, + calculateOperatorChurnApprovalDigestHash( + registeringOperator, + registeringOperatorId, + operatorKickParams, + churnApproverSignature.salt, + churnApproverSignature.expiry + ), churnApproverSignature.signature ); } @@ -717,19 +933,49 @@ contract RegistryCoordinator is function _createQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams + IStakeRegistry.StrategyParams[] memory strategyParams, + StakeType stakeType, + uint32 lookAheadPeriod ) internal { // Increment the total quorum count. Fails if we're already at the max uint8 prevQuorumCount = quorumCount; - require(prevQuorumCount < MAX_QUORUM_COUNT, "RegistryCoordinator.createQuorum: max quorums reached"); + require( + prevQuorumCount < MAX_QUORUM_COUNT, + MaxQuorumsReached() + ); quorumCount = prevQuorumCount + 1; - + // The previous count is the new quorum's number uint8 quorumNumber = prevQuorumCount; // Initialize the quorum here and in each registry _setOperatorSetParams(quorumNumber, operatorSetParams); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + + /// Update the AllocationManager if operatorSetQuorum + if (isOperatorSetAVS && !isM2Quorum[quorumNumber]) { + // Create array of CreateSetParams for the new quorum + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + + // Extract strategies from strategyParams + IStrategy[] memory strategies = new IStrategy[](strategyParams.length); + for (uint256 i = 0; i < strategyParams.length; i++) { + strategies[i] = strategyParams[i].strategy; + } + + // Initialize CreateSetParams with quorumNumber as operatorSetId + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: quorumNumber, + strategies: strategies + }); + serviceManager.createOperatorSets(createSetParams); + } + // Initialize stake registry based on stake type + if (stakeType == StakeType.TOTAL_DELEGATED) { + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + } else if (stakeType == StakeType.TOTAL_SLASHABLE) { + stakeRegistry.initializeSlashableStakeQuorum(quorumNumber, minimumStake, lookAheadPeriod, strategyParams); + } + indexRegistry.initializeQuorum(quorumNumber); blsApkRegistry.initializeQuorum(quorumNumber); } @@ -739,46 +985,13 @@ contract RegistryCoordinator is * @param newBitmap is the most up-to-date set of bitmaps the operator is registered for */ function _updateOperatorBitmap(bytes32 operatorId, uint192 newBitmap) internal { - - uint256 historyLength = _operatorBitmapHistory[operatorId].length; - - if (historyLength == 0) { - // No prior bitmap history - push our first entry - _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: newBitmap - })); - } else { - // We have prior history - fetch our last-recorded update - QuorumBitmapUpdate storage lastUpdate = _operatorBitmapHistory[operatorId][historyLength - 1]; - - /** - * If the last update was made in the current block, update the entry. - * Otherwise, push a new entry and update the previous entry's "next" field - */ - if (lastUpdate.updateBlockNumber == uint32(block.number)) { - lastUpdate.quorumBitmap = newBitmap; - } else { - lastUpdate.nextUpdateBlockNumber = uint32(block.number); - _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: newBitmap - })); - } - } + QuorumBitmapHistoryLib.updateOperatorBitmap(_operatorBitmapHistory, operatorId, newBitmap); } /// @notice Get the most recent bitmap for the operator, returning an empty bitmap if /// the operator is not registered. function _currentOperatorBitmap(bytes32 operatorId) internal view returns (uint192) { - uint256 historyLength = _operatorBitmapHistory[operatorId].length; - if (historyLength == 0) { - return 0; - } else { - return _operatorBitmapHistory[operatorId][historyLength - 1].quorumBitmap; - } + return QuorumBitmapHistoryLib.currentOperatorBitmap(_operatorBitmapHistory, operatorId); } /** @@ -787,31 +1000,20 @@ contract RegistryCoordinator is * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function */ function _getQuorumBitmapIndexAtBlockNumber( - uint32 blockNumber, + uint32 blockNumber, bytes32 operatorId ) internal view returns (uint32 index) { - uint256 length = _operatorBitmapHistory[operatorId].length; - - // Traverse the operator's bitmap history in reverse, returning the first index - // corresponding to an update made before or at `blockNumber` - for (uint256 i = 0; i < length; i++) { - index = uint32(length - i - 1); - - if (_operatorBitmapHistory[operatorId][index].updateBlockNumber <= blockNumber) { - return index; - } - } - - revert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" - ); + return QuorumBitmapHistoryLib.getQuorumBitmapIndexAtBlockNumber(_operatorBitmapHistory,blockNumber, operatorId); } - function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal { + function _setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) internal { _quorumParams[quorumNumber] = operatorSetParams; emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); } - + function _setChurnApprover(address newChurnApprover) internal { emit ChurnApproverUpdated(churnApprover, newChurnApprover); churnApprover = newChurnApprover; @@ -822,12 +1024,18 @@ contract RegistryCoordinator is ejector = newEjector; } - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ + /** + * + * VIEW FUNCTIONS + * + */ /// @notice Returns the operator set params for the given `quorumNumber` - function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory) { + function getOperatorSetParams(uint8 quorumNumber) + external + view + returns (OperatorSetParam memory) + { return _quorumParams[quorumNumber]; } @@ -847,7 +1055,11 @@ contract RegistryCoordinator is } /// @notice Returns the status for the given `operator` - function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus) { + function getOperatorStatus(address operator) + external + view + returns (IRegistryCoordinator.OperatorStatus) + { return _operatorInfo[operator].status; } @@ -857,14 +1069,10 @@ contract RegistryCoordinator is * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function */ function getQuorumBitmapIndicesAtBlockNumber( - uint32 blockNumber, + uint32 blockNumber, bytes32[] memory operatorIds ) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](operatorIds.length); - for (uint256 i = 0; i < operatorIds.length; i++) { - indices[i] = _getQuorumBitmapIndexAtBlockNumber(blockNumber, operatorIds[i]); - } - return indices; + return QuorumBitmapHistoryLib.getQuorumBitmapIndicesAtBlockNumber(_operatorBitmapHistory, blockNumber, operatorIds); } /** @@ -872,34 +1080,18 @@ contract RegistryCoordinator is * reverting if `index` is incorrect * @dev This function is meant to be used in concert with `getQuorumBitmapIndicesAtBlockNumber`, which * helps off-chain processes to fetch the correct `index` input - */ + */ function getQuorumBitmapAtBlockNumberByIndex( - bytes32 operatorId, - uint32 blockNumber, + bytes32 operatorId, + uint32 blockNumber, uint256 index ) external view returns (uint192) { - QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index]; - - /** - * Validate that the update is valid for the given blockNumber: - * - blockNumber should be >= the update block number - * - the next update block number should be either 0 or strictly greater than blockNumber - */ - require( - blockNumber >= quorumBitmapUpdate.updateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" - ); - require( - quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" - ); - - return quorumBitmapUpdate.quorumBitmap; + return QuorumBitmapHistoryLib.getQuorumBitmapAtBlockNumberByIndex(_operatorBitmapHistory, operatorId, blockNumber, index); } /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history function getQuorumBitmapUpdateByIndex( - bytes32 operatorId, + bytes32 operatorId, uint256 index ) external view returns (QuorumBitmapUpdate memory) { return _operatorBitmapHistory[operatorId][index]; @@ -922,7 +1114,7 @@ contract RegistryCoordinator is /** * @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums - * @param registeringOperatorId The id of the registering operator + * @param registeringOperatorId The id of the registering operator * @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps * @param salt The salt to use for the churnApprover's signature * @param expiry The desired expiry time of the churnApprover's signature @@ -935,17 +1127,45 @@ contract RegistryCoordinator is uint256 expiry ) public view returns (bytes32) { // calculate the digest hash - return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperator, registeringOperatorId, operatorKickParams, salt, expiry))); + return _hashTypedDataV4( + keccak256( + abi.encode( + OPERATOR_CHURN_APPROVAL_TYPEHASH, + registeringOperator, + registeringOperatorId, + operatorKickParams, + salt, + expiry + ) + ) + ); } /** * @notice Returns the message hash that an operator must sign to register their BLS public key. * @param operator is the address of the operator registering their BLS public key */ - function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1( - _hashTypedDataV4( - keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) + function pubkeyRegistrationMessageHash(address operator) + public + view + returns (BN254.G1Point memory) + { + return BN254.hashToG1(calculatePubkeyRegistrationMessageHash(operator)); + } + + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator is the address of the operator registering their BLS public key + */ + function calculatePubkeyRegistrationMessageHash( + address operator + ) public view returns (bytes32) { + return _hashTypedDataV4( + keccak256( + abi.encode( + PUBKEY_REGISTRATION_TYPEHASH, + operator + ) ) ); } diff --git a/src/RegistryCoordinatorStorage.sol b/src/RegistryCoordinatorStorage.sol index 5451efb3..4911da4b 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/RegistryCoordinatorStorage.sol @@ -1,16 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /******************************************************************************* - CONSTANTS AND IMMUTABLES + CONSTANTS AND IMMUTABLES *******************************************************************************/ /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract @@ -39,9 +40,11 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { IStakeRegistry public immutable stakeRegistry; /// @notice the Index Registry contract that will keep track of operators' indexes IIndexRegistry public immutable indexRegistry; + /// @notice the AVS Directory that tracks operator registrations to AVS and operator sets + IAVSDirectory public immutable avsDirectory; /******************************************************************************* - STATE + STATE *******************************************************************************/ /// @notice the current number of quorums supported by the registry coordinator @@ -69,6 +72,14 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @notice the delay in seconds before an operator can reregister after being ejected uint256 public ejectionCooldown; + /// @notice Whether this AVS uses operator sets for registration + /// @dev If true, operators must register to operator sets via the AllocationManager + bool public isOperatorSetAVS; + + /// @notice Mapping from quorum number to whether the quorum is an M2 quorum + /// @dev M2 quorums are pre-operator sets and track total delegated stake only + mapping(uint8 => bool) public isM2Quorum; + constructor( IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, @@ -83,5 +94,5 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[39] private __GAP; + uint256[37] private __GAP; } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 6680edd9..b7a07df8 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -1,16 +1,22 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {LibMergeSort} from "./libraries/LibMergeSort.sol"; /** * @title Minimal implementation of a ServiceManager-type contract. @@ -20,12 +26,11 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol"; abstract contract ServiceManagerBase is ServiceManagerBaseStorage { using BitmapUtils for *; + uint256 public constant SLASHER_PROPOSAL_DELAY = 7 days; + /// @notice when applied to a function, only allows the RegistryCoordinator to call it modifier onlyRegistryCoordinator() { - require( - msg.sender == address(_registryCoordinator), - "ServiceManagerBase.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + require(msg.sender == address(_registryCoordinator), OnlyRegistryCoordinator()); _; } @@ -35,11 +40,16 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _; } - function _checkRewardsInitiator() internal view { - require( - msg.sender == rewardsInitiator, - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + /// @notice only slasher can call functions with this modifier + modifier onlySlasher() { + _checkSlasher(); + _; + } + + /// @notice only StakeRegistry can call functions with this modifier + modifier onlyStakeRegistry() { + _checkStakeRegistry(); + _; } /// @notice Sets the (immutable) `_registryCoordinator` address @@ -47,13 +57,15 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, IRegistryCoordinator __registryCoordinator, - IStakeRegistry __stakeRegistry + IStakeRegistry __stakeRegistry, + IAllocationManager __allocationManager ) ServiceManagerBaseStorage( __avsDirectory, __rewardsCoordinator, __registryCoordinator, - __stakeRegistry + __stakeRegistry, + __allocationManager ) { _disableInitializers(); @@ -61,10 +73,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { function __ServiceManagerBase_init( address initialOwner, - address _rewardsInitiator + address _rewardsInitiator, + address _slasher ) internal virtual onlyInitializing { _transferOwnership(initialOwner); _setRewardsInitiator(_rewardsInitiator); + _setSlasher(_slasher); } /** @@ -76,26 +90,30 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _avsDirectory.updateAVSMetadataURI(_metadataURI); } + function slashOperator(IAllocationManager.SlashingParams memory params) external onlySlasher { + _allocationManager.slashOperator(address(this), params); + } + /** * @notice Creates a new rewards submission to the EigenLayer RewardsCoordinator contract, to be split amongst the * set of stakers delegated to operators who are registered to this `avs` * @param rewardsSubmissions The rewards submissions being created - * @dev Only callabe by the permissioned rewardsInitiator address + * @dev Only callable by the permissioned rewardsInitiator address * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` * @dev The tokens are sent to the `RewardsCoordinator` contract * @dev Strategies must be in ascending order of addresses to check for duplicates * @dev This function will revert if the `rewardsSubmission` is malformed, * e.g. if the `strategies` and `weights` arrays are of non-equal lengths */ - function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) - public - virtual - onlyRewardsInitiator - { + function createAVSRewardsSubmission( + IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions + ) public virtual onlyRewardsInitiator { for (uint256 i = 0; i < rewardsSubmissions.length; ++i) { // transfer token to ServiceManager and approve RewardsCoordinator to transfer again // in createAVSRewardsSubmission() call - rewardsSubmissions[i].token.transferFrom(msg.sender, address(this), rewardsSubmissions[i].amount); + rewardsSubmissions[i].token.transferFrom( + msg.sender, address(this), rewardsSubmissions[i].amount + ); uint256 allowance = rewardsSubmissions[i].token.allowance(address(this), address(_rewardsCoordinator)); rewardsSubmissions[i].token.approve( @@ -106,6 +124,18 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); } + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) external onlyRegistryCoordinator { + _allocationManager.createOperatorSets(address(this), params); + } + + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external onlyStakeRegistry { + _allocationManager.addStrategiesToOperatorSet(address(this), operatorSetId, strategies); + } + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external onlyStakeRegistry { + _allocationManager.removeStrategiesFromOperatorSet(address(this), operatorSetId, strategies); + } + /** * @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. @@ -126,6 +156,22 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _avsDirectory.deregisterOperatorFromAVS(operator); } + /** + * @notice Forwards a call to EigenLayer's AVSDirectory contract to deregister an operator from operator sets + * @param operator The address of the operator to deregister. + * @param operatorSetIds The IDs of the operator sets. + */ + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] calldata operatorSetIds + ) public virtual onlyRegistryCoordinator { + _allocationManager.deregisterFromOperatorSets(IAllocationManagerTypes.DeregisterParams({ + operator: operator, + avs: address(this), + operatorSetIds: operatorSetIds + })); + } + /** * @notice Sets the rewards initiator address * @param newRewardsInitiator The new rewards initiator address @@ -135,11 +181,53 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _setRewardsInitiator(newRewardsInitiator); } + /** + * @notice Sets the AVS registrar address in the AllocationManager + * @param registrar The new AVS registrar address + * @dev Only callable by the registry coordinator + */ + function setAVSRegistrar(IAVSRegistrar registrar) external onlyRegistryCoordinator { + _allocationManager.setAVSRegistrar(address(this), registrar); + } + + /** + * @notice Proposes a new slasher address + * @param newSlasher The new slasher address + * @dev only callable by the owner + */ + function proposeNewSlasher(address newSlasher) external onlyOwner { + _proposeNewSlasher(newSlasher); + } + + /** + * @notice Accepts the proposed slasher address after the delay period + * @dev only callable by the owner + */ + function acceptProposedSlasher() external onlyOwner { + require( + block.timestamp >= slasherProposalTimestamp + SLASHER_PROPOSAL_DELAY, + DelayPeriodNotPassed() + ); + _setSlasher(proposedSlasher); + delete proposedSlasher; + } + function _setRewardsInitiator(address newRewardsInitiator) internal { emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator); rewardsInitiator = newRewardsInitiator; } + function _proposeNewSlasher(address newSlasher) internal { + proposedSlasher = newSlasher; + slasherProposalTimestamp = block.timestamp; + emit SlasherProposed(newSlasher, slasherProposalTimestamp); + } + + function _setSlasher(address newSlasher) internal { + emit SlasherUpdated(slasher, newSlasher); + slasher = newSlasher; + } + /** * @notice Returns the list of strategies that the AVS supports for restaking * @dev This function is intended to be called off-chain @@ -217,4 +305,20 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { function avsDirectory() external view override returns (address) { return address(_avsDirectory); } + + function allocationManager() external view override returns (address) { + return address(_allocationManager); + } + + function _checkRewardsInitiator() internal view { + require(msg.sender == rewardsInitiator, OnlyRewardsInitiator()); + } + + function _checkStakeRegistry() internal view { + require(msg.sender == address(_stakeRegistry), OnlyStakeRegistry()); + } + + function _checkSlasher() internal view { + require(msg.sender == slasher, OnlySlasher()); + } } diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index e0c1d86a..6b8ee27b 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; @@ -9,6 +9,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; /** * @title Storage variables for the `ServiceManagerBase` contract. @@ -25,6 +26,7 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab IRewardsCoordinator internal immutable _rewardsCoordinator; IRegistryCoordinator internal immutable _registryCoordinator; IStakeRegistry internal immutable _stakeRegistry; + IAllocationManager internal immutable _allocationManager; /** * @@ -35,19 +37,33 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab /// @notice The address of the entity that can initiate rewards address public rewardsInitiator; - /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses + /// @notice The address of the slasher account + address public slasher; + + /// @notice The address of the proposed slasher account + address public proposedSlasher; + + /// @notice The timestamp when the slasher was proposed + uint256 public slasherProposalTimestamp; + + /// @notice Boolean indicating if the migration has been finalized + bool public migrationFinalized; + + /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, `_stakeRegistry`, and `_allocationManager` addresses constructor( IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, IRegistryCoordinator __registryCoordinator, - IStakeRegistry __stakeRegistry + IStakeRegistry __stakeRegistry, + IAllocationManager __allocationManager ) { _avsDirectory = __avsDirectory; _rewardsCoordinator = __rewardsCoordinator; _registryCoordinator = __registryCoordinator; _stakeRegistry = __stakeRegistry; + _allocationManager = __allocationManager; } // storage gap for upgradeability - uint256[49] private __GAP; + uint256[45] private __GAP; } diff --git a/src/ServiceManagerRouter.sol b/src/ServiceManagerRouter.sol index e2259cfb..05a56780 100644 --- a/src/ServiceManagerRouter.sol +++ b/src/ServiceManagerRouter.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IServiceManagerUI} from "./interfaces/IServiceManagerUI.sol"; /** * @title Contract that proxies calls to a ServiceManager contract. * This contract is designed to be used by off-chain services which need - * errors to be handled gracefully. + * errors to be handled gracefully. * @author Layr Labs, Inc. */ @@ -40,7 +40,7 @@ contract ServiceManagerRouter { /** * @notice Internal helper function to make static calls - * @dev Handles calls to contracts that don't implement the given function and to EOAs by + * @dev Handles calls to contracts that don't implement the given function and to EOAs by * returning a failed call address */ function _makeCall(address serviceManager, bytes memory data) internal view returns (address[] memory) { diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 929b67d9..162a9149 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -1,12 +1,16 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; @@ -22,7 +26,7 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol"; contract StakeRegistry is StakeRegistryStorage { using BitmapUtils for *; - + modifier onlyRegistryCoordinator() { _checkRegistryCoordinator(); _; @@ -40,8 +44,10 @@ contract StakeRegistry is StakeRegistryStorage { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager - ) StakeRegistryStorage(_registryCoordinator, _delegationManager) {} + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IServiceManager _serviceManager + ) StakeRegistryStorage(_registryCoordinator, _delegationManager, _avsDirectory, _serviceManager) {} /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR @@ -68,22 +74,19 @@ contract StakeRegistry is StakeRegistryStorage { uint96[] memory currentStakes = new uint96[](quorumNumbers.length); uint96[] memory totalStakes = new uint96[](quorumNumbers.length); - for (uint256 i = 0; i < quorumNumbers.length; i++) { - + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); _checkQuorumExists(quorumNumber); // Retrieve the operator's current weighted stake for the quorum, reverting if they have not met // the minimum. (uint96 currentStake, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); - require( - hasMinimumStake, - "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" - ); + require(hasMinimumStake, BelowMinimumStakeRequirement()); // Update the operator's stake int256 stakeDelta = _recordOperatorStakeUpdate({ - operatorId: operatorId, + operatorId: operatorId, quorumNumber: quorumNumber, newStake: currentStake }); @@ -122,8 +125,8 @@ contract StakeRegistry is StakeRegistryStorage { // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ - operatorId: operatorId, - quorumNumber: quorumNumber, + operatorId: operatorId, + quorumNumber: quorumNumber, newStake: 0 }); @@ -142,8 +145,8 @@ contract StakeRegistry is StakeRegistryStorage { * and should be deregistered. */ function updateOperatorStake( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes calldata quorumNumbers ) external onlyRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; @@ -163,8 +166,8 @@ contract StakeRegistry is StakeRegistryStorage { // Fetch the operator's current stake, applying weighting parameters and checking // against the minimum stake requirements for the quorum. (uint96 stakeWeight, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); - // If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal + /// also handle setting the operator's stake to 0 and remove them from the quorum if (!hasMinimumStake) { stakeWeight = 0; quorumsToRemove = uint192(quorumsToRemove.setBit(quorumNumber)); @@ -186,14 +189,36 @@ contract StakeRegistry is StakeRegistryStorage { } /// @notice Initialize a new quorum and push its first history update - function initializeQuorum( + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory _strategyParams + ) public virtual onlyRegistryCoordinator { + require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); + _addStrategyParams(quorumNumber, _strategyParams); + _setMinimumStakeForQuorum(quorumNumber, minimumStake); + _setStakeType(quorumNumber, StakeType.TOTAL_DELEGATED); + + _totalStakeHistory[quorumNumber].push(StakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: 0 + })); + } + + + /// @notice Initialize a new quorum and push its first history update + function initializeSlashableStakeQuorum( uint8 quorumNumber, uint96 minimumStake, + uint32 lookAheadPeriod, StrategyParams[] memory _strategyParams ) public virtual onlyRegistryCoordinator { - require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists"); + require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); + _setStakeType(quorumNumber, StakeType.TOTAL_SLASHABLE); + _setLookAheadPeriod(quorumNumber, lookAheadPeriod); _totalStakeHistory[quorumNumber].push(StakeUpdate({ updateBlockNumber: uint32(block.number), @@ -203,23 +228,42 @@ contract StakeRegistry is StakeRegistryStorage { } function setMinimumStakeForQuorum( - uint8 quorumNumber, + uint8 quorumNumber, uint96 minimumStake ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _setMinimumStakeForQuorum(quorumNumber, minimumStake); } - /** + /** + * @notice Sets the look ahead time for checking operator shares for a specific quorum + * @param quorumNumber The quorum number to set the look ahead period for + * @param _lookAheadPeriod The number of days to look ahead when checking shares + */ + function setSlashableStakeLookahead(uint8 quorumNumber, uint32 _lookAheadPeriod) external onlyCoordinatorOwner quorumExists(quorumNumber) { + _setLookAheadPeriod(quorumNumber, _lookAheadPeriod); + } + + /** * @notice Adds strategies and weights to the quorum * @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies). * @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a concious choice, * since a middleware may want, e.g., a stablecoin quorum that accepts USDC, USDT, DAI, etc. as underlying assets and trades them as "equivalent". */ function addStrategies( - uint8 quorumNumber, + uint8 quorumNumber, StrategyParams[] memory _strategyParams ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _addStrategyParams(quorumNumber, _strategyParams); + + uint256 numStratsToAdd = _strategyParams.length; + + if (isOperatorSetQuorum(quorumNumber)){ + IStrategy[] memory strategiesToAdd = new IStrategy[](numStratsToAdd); + for (uint256 i = 0; i < numStratsToAdd; i++) { + strategiesToAdd[i] = _strategyParams[i].strategy; + } + serviceManager.addStrategyToOperatorSet(quorumNumber, strategiesToAdd); + } } /** @@ -232,21 +276,29 @@ contract StakeRegistry is StakeRegistryStorage { uint256[] memory indicesToRemove ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { uint256 toRemoveLength = indicesToRemove.length; - require(toRemoveLength > 0, "StakeRegistry.removeStrategies: no indices to remove provided"); + require(toRemoveLength > 0, InputArrayLengthZero()); StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; IStrategy[] storage _strategiesPerQuorum = strategiesPerQuorum[quorumNumber]; + IStrategy[] memory _strategiesToRemove = new IStrategy[](toRemoveLength); for (uint256 i = 0; i < toRemoveLength; i++) { + _strategiesToRemove[i]=_strategyParams[indicesToRemove[i]].strategy; emit StrategyRemovedFromQuorum(quorumNumber, _strategyParams[indicesToRemove[i]].strategy); emit StrategyMultiplierUpdated(quorumNumber, _strategyParams[indicesToRemove[i]].strategy, 0); + + // Replace index to remove with the last item in the list, then pop the last item _strategyParams[indicesToRemove[i]] = _strategyParams[_strategyParams.length - 1]; _strategyParams.pop(); _strategiesPerQuorum[indicesToRemove[i]] = _strategiesPerQuorum[_strategiesPerQuorum.length - 1]; _strategiesPerQuorum.pop(); } + + if (isOperatorSetQuorum(quorumNumber)){ + serviceManager.removeStrategiesFromOperatorSet(quorumNumber, _strategiesToRemove); + } } /** @@ -261,8 +313,8 @@ contract StakeRegistry is StakeRegistryStorage { uint96[] calldata newMultipliers ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { uint256 numStrats = strategyIndices.length; - require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); - require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); + require(numStrats > 0, InputArrayLengthZero()); + require(newMultipliers.length == numStrats, InputArrayLengthMismatch()); StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; @@ -324,7 +376,7 @@ contract StakeRegistry is StakeRegistryStorage { })); } else { // We have prior stake history - fetch our last-recorded stake - StakeUpdate storage lastUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength-1]; + StakeUpdate storage lastUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength-1]; prevStake = lastUpdate.stake; // Short-circuit in case there's no change in stake @@ -335,7 +387,7 @@ contract StakeRegistry is StakeRegistryStorage { /** * If our last stake entry was made in the current block, update the entry * Otherwise, push a new entry and update the previous entry's "next" field - */ + */ if (lastUpdate.updateBlockNumber == uint32(block.number)) { lastUpdate.stake = newStake; } else { @@ -364,7 +416,7 @@ contract StakeRegistry is StakeRegistryStorage { if (stakeDelta == 0) { return lastStakeUpdate.stake; } - + // Calculate the new total stake by applying the delta to our previous stake uint96 newStake = _applyDelta(lastStakeUpdate.stake, stakeDelta); @@ -386,7 +438,7 @@ contract StakeRegistry is StakeRegistryStorage { return newStake; } - /** + /** * @notice Adds `strategyParams` to the `quorumNumber`-th quorum. * @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies). * @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a conscious choice, @@ -396,25 +448,22 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, StrategyParams[] memory _strategyParams ) internal { - require(_strategyParams.length > 0, "StakeRegistry._addStrategyParams: no strategies provided"); + require(_strategyParams.length > 0, InputArrayLengthZero()); uint256 numStratsToAdd = _strategyParams.length; uint256 numStratsExisting = strategyParams[quorumNumber].length; require( numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, - "StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH" + InputArrayLengthMismatch() ); for (uint256 i = 0; i < numStratsToAdd; i++) { // fairly gas-expensive internal loop to make sure that the *same* strategy cannot be added multiple times for (uint256 j = 0; j < (numStratsExisting + i); j++) { require( strategyParams[quorumNumber][j].strategy != _strategyParams[i].strategy, - "StakeRegistry._addStrategyParams: cannot add same strategy 2x" + InputDuplicateStrategy() ); } - require( - _strategyParams[i].multiplier > 0, - "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" - ); + require(_strategyParams[i].multiplier > 0, InputMultiplierZero()); strategyParams[quorumNumber].push(_strategyParams[i]); strategiesPerQuorum[quorumNumber].push(_strategyParams[i].strategy); emit StrategyAddedToQuorum(quorumNumber, _strategyParams[i].strategy); @@ -450,16 +499,30 @@ contract StakeRegistry is StakeRegistryStorage { * - blockNumber should be >= the update block number * - the next update block number should be either 0 or strictly greater than blockNumber */ - require( - blockNumber >= stakeUpdate.updateBlockNumber, - "StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber" - ); + require(blockNumber >= stakeUpdate.updateBlockNumber, InvalidBlockNumber()); require( stakeUpdate.nextUpdateBlockNumber == 0 || blockNumber < stakeUpdate.nextUpdateBlockNumber, - "StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber" + InvalidBlockNumber() ); } + /// Returns total Slashable stake for an operator per strategy that can have the weights applied based on strategy multipliers + function _getSlashableStakePerStrategy(uint8 quorumNumber, address operator) internal view returns (uint256[] memory) { + address[] memory operators = new address[](1); + operators[0] = operator; + uint32 beforeTimestamp = uint32(block.timestamp + slashableStakeLookAheadPerQuorum[quorumNumber]); + + uint256[][] memory slashableShares = IAllocationManager(serviceManager.allocationManager()) + .getMinimumSlashableStake( + OperatorSet(address(serviceManager), quorumNumber), + operators, + strategiesPerQuorum[quorumNumber], + beforeTimestamp + ); + + return slashableShares[0]; + } + /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev this method DOES NOT check that the quorum exists @@ -470,15 +533,27 @@ contract StakeRegistry is StakeRegistryStorage { uint96 weight; uint256 stratsLength = strategyParamsLength(quorumNumber); StrategyParams memory strategyAndMultiplier; - - uint256[] memory strategyShares = delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]); - for (uint256 i = 0; i < stratsLength; i++) { - // accessing i^th StrategyParams struct for the quorumNumber - strategyAndMultiplier = strategyParams[quorumNumber][i]; - - // add the weight from the shares for this strategy to the total weight - if (strategyShares[i] > 0) { - weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + uint256[] memory strategyShares; + + if (stakeTypePerQuorum[quorumNumber]== StakeType.TOTAL_SLASHABLE) { + strategyShares = _getSlashableStakePerStrategy(quorumNumber, operator); + for (uint256 i = 0; i < stratsLength; i++) { + strategyAndMultiplier = strategyParams[quorumNumber][i]; + if (strategyShares[i] > 0) { + weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + } + } + } else { + /// M2 Concept of delegated stake + strategyShares = delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]); + for (uint256 i = 0; i < stratsLength; i++) { + // accessing i^th StrategyParams struct for the quorumNumber + strategyAndMultiplier = strategyParams[quorumNumber][i]; + + // add the weight from the shares for this strategy to the total weight + if (strategyShares[i] > 0) { + weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + } } } @@ -496,12 +571,25 @@ contract StakeRegistry is StakeRegistryStorage { VIEW FUNCTIONS *******************************************************************************/ + /** + * @notice Returns whether a quorum is an operator set quorum based on its stake type + * @dev A quorum is an operator set quorum if it has TOTAL_SLASHABLE stake type + * and is not an M2 quorum + * @param quorumNumber The quorum number to check + * @return True if the quorum is an operator set quorum + */ + function isOperatorSetQuorum(uint8 quorumNumber) public view returns (bool) { + bool isM2 = IRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber); + bool isOperatorSet = IRegistryCoordinator(registryCoordinator).isOperatorSetAVS(); + return isOperatorSet && !isM2; + } + /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev reverts if the quorum does not exist */ function weightOfOperatorForQuorum( - uint8 quorumNumber, + uint8 quorumNumber, address operator ) public virtual view quorumExists(quorumNumber) returns (uint96) { (uint96 stake, ) = _weightOfOperatorForQuorum(quorumNumber, operator); @@ -515,7 +603,7 @@ contract StakeRegistry is StakeRegistryStorage { /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` function strategyParamsByIndex( - uint8 quorumNumber, + uint8 quorumNumber, uint256 index ) public view returns (StrategyParams memory) { @@ -542,7 +630,7 @@ contract StakeRegistry is StakeRegistryStorage { * @param quorumNumber The quorum number to get the stake for. */ function getStakeHistory( - bytes32 operatorId, + bytes32 operatorId, uint8 quorumNumber ) external view returns (StakeUpdate[] memory) { return operatorStakeHistory[operatorId][quorumNumber]; @@ -661,7 +749,7 @@ contract StakeRegistry is StakeRegistryStorage { uint256 index ) external view returns (StakeUpdate memory) { return _totalStakeHistory[quorumNumber][index]; - } + } /** * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the @@ -697,7 +785,7 @@ contract StakeRegistry is StakeRegistryStorage { _checkQuorumExists(quorumNumber); require( _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, - "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" + EmptyStakeHistory() ); uint256 length = _totalStakeHistory[quorumNumber].length; for (uint256 j = 0; j < length; j++) { @@ -710,18 +798,37 @@ contract StakeRegistry is StakeRegistryStorage { return indices; } + /** + * @notice Sets the stake type for the registry for a specific quorum + * @param quorumNumber The quorum number to set the stake type for + * @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH) + */ + function _setStakeType(uint8 quorumNumber, StakeType _stakeType) internal { + stakeTypePerQuorum[quorumNumber] = _stakeType; + emit StakeTypeSet(_stakeType); + } + + /** + * @notice Sets the look ahead time for checking operator shares for a specific quorum + * @param quorumNumber The quorum number to set the look ahead period for + * @param _lookAheadDays The number of days to look ahead when checking shares + */ + function _setLookAheadPeriod(uint8 quorumNumber, uint32 _lookAheadDays) internal { + uint32 oldLookAheadDays = slashableStakeLookAheadPerQuorum[quorumNumber]; + slashableStakeLookAheadPerQuorum[quorumNumber] = _lookAheadDays; + emit LookAheadPeriodChanged(oldLookAheadDays, _lookAheadDays); + } + + function _checkRegistryCoordinator() internal view { - require( - msg.sender == address(registryCoordinator), - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator()); } function _checkRegistryCoordinatorOwner() internal view { - require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), OnlyRegistryCoordinatorOwner()); } function _checkQuorumExists(uint8 quorumNumber) internal view { - require(_quorumExists(quorumNumber), "StakeRegistry.quorumExists: quorum does not exist"); + require(_quorumExists(quorumNumber), QuorumDoesNotExist()); } } diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 6ef74e3e..fb4967ac 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -1,11 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IStrategyManager, IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; /** * @title Storage variables for the `StakeRegistry` contract. @@ -13,7 +15,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; * @notice This storage contract is separate from the logic to simplify the upgrade process. */ abstract contract StakeRegistryStorage is IStakeRegistry { - + /// @notice Constant used as a divisor in calculating weights. uint256 public constant WEIGHTING_DIVISOR = 1e18; /// @notice Maximum length of dynamic arrays in the `strategyParams` mapping. @@ -24,6 +26,12 @@ abstract contract StakeRegistryStorage is IStakeRegistry { /// @notice The address of the Delegation contract for EigenLayer. IDelegationManager public immutable delegation; + /// @notice The address of the Delegation contract for EigenLayer. + IAVSDirectory public immutable avsDirectory; + + /// @notice the address of the ServiceManager associtated with the stake registries + IServiceManager public immutable serviceManager; + /// @notice the coordinator contract that this registry is associated with address public immutable registryCoordinator; @@ -44,16 +52,23 @@ abstract contract StakeRegistryStorage is IStakeRegistry { mapping(uint8 => StrategyParams[]) public strategyParams; mapping(uint8 => IStrategy[]) public strategiesPerQuorum; + mapping(uint8 => StakeType) public stakeTypePerQuorum; + + mapping(uint8 => uint32) public slashableStakeLookAheadPerQuorum; constructor( - IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager + IRegistryCoordinator _registryCoordinator, + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IServiceManager _serviceManager ) { registryCoordinator = address(_registryCoordinator); delegation = _delegationManager; + avsDirectory = _avsDirectory; + serviceManager = _serviceManager; } // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[45] private __GAP; + uint256[43] private __GAP; } diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 2812a0ce..a93d4d53 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -1,15 +1,38 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistry} from "./IRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; +interface IBLSApkRegistryErrors { + /// @dev Thrown when the caller is not the owner of the registry coordinator. + error OnlyRegistryCoordinatorOwner(); + /// @dev Thrown when a quorum being created already exists. + error QuorumAlreadyExists(); + /// @dev Thrown when a quorum does not exist. + error QuorumDoesNotExist(); + /// @dev Thrown when a BLS pubkey provided is zero pubkey + error ZeroPubKey(); + /// @dev Thrown when an operator has already registered a BLS pubkey. + error OperatorAlreadyRegistered(); + /// @dev Thrown when the operator is not registered. + error OperatorNotRegistered(); + /// @dev Thrown when a BLS pubkey has already been registered for an operator. + error BLSPubkeyAlreadyRegistered(); + /// @dev Thrown when either the G1 signature is wrong, or G1 and G2 private key do not match. + error InvalidBLSSignatureOrPrivateKey(); + /// @dev Thrown when the quorum apk update block number is too recent. + error BlockNumberTooRecent(); + /// @dev Thrown when blocknumber and index provided is not the latest apk update. + error BlockNumberNotLatest(); +} + /** * @title Minimal interface for a registry that keeps track of aggregate operator public keys across many quorums. * @author Layr Labs, Inc. */ -interface IBLSApkRegistry is IRegistry { +interface IBLSApkRegistry is IRegistry, IBLSApkRegistryErrors { // STRUCTS /// @notice Data structure used to track the history of the Aggregate Public Key of all operators struct ApkUpdate { @@ -24,9 +47,9 @@ interface IBLSApkRegistry is IRegistry { /** * @notice Struct used when registering a new public key * @param pubkeyRegistrationSignature is the registration message signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator + * @param pubkeyG1 is the corresponding G1 public key of the operator * @param pubkeyG2 is the corresponding G2 public key of the operator - */ + */ struct PubkeyRegistrationParams { BN254.G1Point pubkeyRegistrationSignature; BN254.G1Point pubkeyG1; @@ -46,7 +69,7 @@ interface IBLSApkRegistry is IRegistry { // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); @@ -75,9 +98,9 @@ interface IBLSApkRegistry is IRegistry { * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already deregistered * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ + */ function deregisterOperator(address operator, bytes calldata quorumNumbers) external; - + /** * @notice Initializes a new quorum by pushing its first apk update * @param quorumNumber The number of the new quorum diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 844c7217..4ab73f4c 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; @@ -7,13 +7,36 @@ import {IStakeRegistry, IDelegationManager} from "./IStakeRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; +interface IBLSSignatureCheckerErrors { + /// @dev Thrown when the caller is not the registry coordinator owner. + error OnlyRegistryCoordinatorOwner(); + /// @dev Thrown when the quorum numbers input in is empty. + error InputEmptyQuorumNumbers(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when the non-signer pubkey length does not match non-signer bitmap indices length. + error InputNonSignerLengthMismatch(); + /// @dev Thrown when the reference block number is invalid. + error InvalidReferenceBlocknumber(); + /// @dev Thrown when the non signer pubkeys are not sorted. + error NonSignerPubkeysNotSorted(); + /// @dev Thrown when StakeRegistry updates have not been updated within withdrawalDelayBlocks window + error StaleStakesForbidden(); + /// @dev Thrown when the quorum apk hash in storage does not match provided quorum apk. + error InvalidQuorumApkHash(); + /// @dev Thrown when BLS pairing precompile call fails. + error InvalidBLSPairingKey(); + /// @dev Thrown when BLS signature is invalid. + error InvalidBLSSignature(); +} + /** * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSApkRegistry/StakeRegistry architechture. * @author Layr Labs, Inc. * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service * @notice This is the contract for checking the validity of aggregate operator signatures. */ -interface IBLSSignatureChecker { +interface IBLSSignatureChecker is IBLSSignatureCheckerErrors { // DATA STRUCTURES struct NonSignerStakesAndSignature { @@ -42,8 +65,8 @@ interface IBLSSignatureChecker { // EVENTS /// @notice Emitted when `staleStakesForbiddenUpdate` is set - event StaleStakesForbiddenUpdate(bool value); - + event StaleStakesForbiddenUpdate(bool value); + // CONSTANTS & IMMUTABLES function registryCoordinator() external view returns (IRegistryCoordinator); @@ -59,21 +82,21 @@ interface IBLSSignatureChecker { * The thesis of this procedure entails: * - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the * disperser (represented by apk in the parameters), - * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing + * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing * the output in apk to get aggregated pubkey of all operators that are part of quorum. * - use this aggregated pubkey to verify the aggregated signature under BLS scheme. - * + * * @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber` * is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update * for the total stake (or the operator) or latest before the referenceBlockNumber. */ function checkSignatures( - bytes32 msgHash, + bytes32 msgHash, bytes calldata quorumNumbers, - uint32 referenceBlockNumber, + uint32 referenceBlockNumber, NonSignerStakesAndSignature memory nonSignerStakesAndSignature - ) - external + ) + external view returns ( QuorumStakeTotals memory, diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol index 445db814..b43743d3 100644 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; diff --git a/src/interfaces/IEjectionManager.sol b/src/interfaces/IEjectionManager.sol index 9a02991e..478e21d8 100644 --- a/src/interfaces/IEjectionManager.sol +++ b/src/interfaces/IEjectionManager.sol @@ -1,11 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; + +interface IEjectionManagerErrors { + /// @dev Thrown when the caller is not the owner or ejector. + error OnlyOwnerOrEjector(); + /// @dev Thrown when quorum number exceeds MAX_QUORUM_COUNT. + error MaxQuorumCount(); +} /** * @title Interface for a contract that ejects operators from an AVSs RegistryCoordinator * @author Layr Labs, Inc. */ -interface IEjectionManager { +interface IEjectionManager is IEjectionManagerErrors { /// @notice A quorum's ratelimit parameters struct QuorumEjectionParams { @@ -25,7 +32,7 @@ interface IEjectionManager { event QuorumEjectionParamsSet(uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent); ///@notice Emitted when an operator is ejected event OperatorEjected(bytes32 operatorId, uint8 quorumNumber); - ///@notice Emitted when operators are ejected for a quroum + ///@notice Emitted when operators are ejected for a quroum event QuorumEjection(uint32 ejectedOperators, bool ratelimitHit); /** diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 72a702d8..df1e0e73 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -1,21 +1,30 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistry} from "./IRegistry.sol"; +interface IIndexRegistryErrors { + /// @dev Thrown when a function is called by an address that is not the RegistryCoordinator + error OnlyRegistryCoordinator(); + /// @dev Thrown when a quorum has 0 length history and thus does not exist + error QuorumDoesNotExist(); + /// @dev Thrown when an operatorId is not found in the registry at a given block number + error OperatorIdDoesNotExist(); +} + /** * @title Interface for a `Registry`-type contract that keeps track of an ordered list of operators for up to 256 quorums. * @author Layr Labs, Inc. */ -interface IIndexRegistry is IRegistry { +interface IIndexRegistry is IRegistry, IIndexRegistryErrors { // EVENTS - + // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex); // DATA STRUCTURES - // struct used to give definitive ordering to operators at each blockNumber. + // struct used to give definitive ordering to operators at each blockNumber. struct OperatorUpdate { // blockNumber number from which `operatorIndex` was the operators index // the operator's index is the first entry such that `blockNumber >= entry.fromBlockNumber` diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 43fa7e0a..4ce2e917 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,16 +1,41 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; +import {IServiceManager} from "./IServiceManager.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; import {IStakeRegistry} from "./IStakeRegistry.sol"; import {IIndexRegistry} from "./IIndexRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; +interface IRegistryCoordinatorErrors { + error InputLengthMismatch(); + error OperatorSetsEnabled(); + error OperatorSetsNotEnabled(); + error OperatorSetsNotSupported(); + error OnlyAllocationManager(); + error OnlyEjector(); + error QuorumDoesNotExist(); + error BitmapEmpty(); + error AlreadyRegisteredForQuorums(); + error CannotReregisterYet(); + error NotRegistered(); + error CannotChurnSelf(); + error QuorumOperatorCountMismatch(); + error InsufficientStakeForChurn(); + error CannotKickOperatorAboveThreshold(); + error BitmapCannotBeZero(); + error NotRegisteredForQuorum(); + error MaxQuorumsReached(); + error SaltAlreadyUsed(); + error RegistryCoordinatorSignatureExpired(); + error ChurnApproverSaltUsed(); + error NotSorted(); +} /** * @title Interface for a contract that coordinates between various registries for an AVS. * @author Layr Labs, Inc. */ -interface IRegistryCoordinator { +interface IRegistryCoordinator is IRegistryCoordinatorErrors{ // EVENTS /// Emits when an operator is registered @@ -50,7 +75,7 @@ interface IRegistryCoordinator { } /** - * @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the + * @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the * quorums the operator is registered for starting at (inclusive)`updateBlockNumber` and ending at (exclusive) `nextUpdateBlockNumber` * @dev nextUpdateBlockNumber is initialized to 0 for the latest update */ @@ -61,11 +86,11 @@ interface IRegistryCoordinator { } /** - * @notice Data structure for storing operator set params for a given quorum. Specifically the + * @notice Data structure for storing operator set params for a given quorum. Specifically the * `maxOperatorCount` is the maximum number of operators that can be registered for the quorum, * `kickBIPsOfOperatorStake` is the basis points of a new operator needs to have of an operator they are trying to kick from the quorum, * and `kickBIPsOfTotalStake` is the basis points of the total stake of the quorum that an operator needs to be below to be kicked. - */ + */ struct OperatorSetParam { uint32 maxOperatorCount; uint16 kickBIPsOfOperatorStake; @@ -96,7 +121,7 @@ interface IRegistryCoordinator { * @param quorumNumbers are the quorum numbers to eject the operator from */ function ejectOperator( - address operator, + address operator, bytes calldata quorumNumbers ) external; @@ -120,8 +145,8 @@ interface IRegistryCoordinator { /** * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index` - * @dev reverts if `index` is incorrect - */ + * @dev reverts if `index` is incorrect + */ function getQuorumBitmapAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192); /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history @@ -139,6 +164,15 @@ interface IRegistryCoordinator { /// @notice Returns the number of registries function numRegistries() external view returns (uint256); + /// @notice Returns whether a quorum is an M2 quorum + /// @param quorumNumber The quorum number to check + /// @return True if the quorum is an M2 quorum + function isM2Quorum(uint8 quorumNumber) external view returns (bool); + + /// @notice Returns whether the AVS is an operator set AVS + /// @return True if the AVS is an operator set AVS + function isOperatorSetAVS() external view returns (bool); + /** * @notice Returns the message hash that an operator must sign to register their BLS public key. * @param operator is the address of the operator registering their BLS public key @@ -150,4 +184,6 @@ interface IRegistryCoordinator { /// @notice The owner of the registry coordinator function owner() external view returns (address); + + function serviceManager() external view returns (IServiceManager); } diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index ad953ec0..ccc0603f 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -3,17 +3,35 @@ pragma solidity >=0.5.0; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {IServiceManagerUI} from "./IServiceManagerUI.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + +interface IServiceManagerErrors { + /// @dev Thrown when a function is called by an address that is not the RegistryCoordinator + error OnlyRegistryCoordinator(); + /// @dev Thrown when a function is called by an address that is not the RewardsInitiator + error OnlyRewardsInitiator(); + /// @dev Thrown when a function is called by an address that is not the Slasher + error OnlyStakeRegistry(); + /// @dev Thrown when a function is called by an address that is not the Slasher + error OnlySlasher(); + /// @dev Thrown when a slashing proposal delay has not been met yet. + error DelayPeriodNotPassed(); +} /** * @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer * @author Layr Labs, Inc. */ -interface IServiceManager is IServiceManagerUI { +interface IServiceManager is IServiceManagerUI, IServiceManagerErrors { /** * @notice Creates a new rewards submission to the EigenLayer RewardsCoordinator contract, to be split amongst the * set of stakers delegated to operators who are registered to this `avs` * @param rewardsSubmissions The rewards submissions being created - * @dev Only callabe by the permissioned rewardsInitiator address + * @dev Only callable by the permissioned rewardsInitiator address * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` * @dev The tokens are sent to the `RewardsCoordinator` contract * @dev Strategies must be in ascending order of addresses to check for duplicates @@ -22,6 +40,30 @@ interface IServiceManager is IServiceManagerUI { */ function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external; + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) external; + + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external; + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external; + + /** + * @notice Sets the AVS registrar address in the AllocationManager + * @param registrar The new AVS registrar address + * @dev Only callable by the registry coordinator + */ + function setAVSRegistrar(IAVSRegistrar registrar) external; + + /** + * @notice Forwards a call to EigenLayer's AVSDirectory contract to deregister an operator from operator sets + * @param operator The address of the operator to deregister. + * @param operatorSetIds The IDs of the operator sets. + */ + function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external; + + function slashOperator(IAllocationManagerTypes.SlashingParams memory params) external; + // EVENTS event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); + event SlasherUpdated(address prevSlasher, address newSlasher); + event SlasherProposed(address newSlasher, uint256 slasherProposalTimestamp); } diff --git a/src/interfaces/IServiceManagerUI.sol b/src/interfaces/IServiceManagerUI.sol index 92cdce9c..7be5a3f0 100644 --- a/src/interfaces/IServiceManagerUI.sol +++ b/src/interfaces/IServiceManagerUI.sol @@ -58,4 +58,7 @@ interface IServiceManagerUI { /// @notice Returns the EigenLayer AVSDirectory contract. function avsDirectory() external view returns (address); + + /// @notice Returns the EigenLayer AllocationManager contract. + function allocationManager() external view returns (address); } diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol new file mode 100644 index 00000000..44222150 --- /dev/null +++ b/src/interfaces/ISlasher.sol @@ -0,0 +1,59 @@ + +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +interface ISlasherEvents { + event SlashingRequested( + uint256 indexed requestId, + address indexed operator, + uint32 indexed operatorSetId, + uint256[] wadsToSlash, + string description + ); + + event SlashingRequestCancelled(uint256 indexed requestId); + + event OperatorSlashed( + uint256 indexed slashingRequestId, + address indexed operator, + uint32 indexed operatorSetId, + uint256[] wadsToSlash, + string description + ); +} + +interface ISlasherErrors { + /// @dev Thrown when the caller is not the veto committee + error OnlyVetoCommittee(); + /// @dev Thrown when the caller is not the slasher + error OnlySlasher(); + /// @dev Thrown when the veto period has passed + error VetoPeriodPassed(); + /// @dev Thrown when the veto period has not passed + error VetoPeriodNotPassed(); + /// @dev Thrown when the slashing request is cancelled + error SlashingRequestIsCancelled(); + /// @dev Thrown when the slashing request was not already requested + error SlashingRequestNotRequested(); +} + +interface ISlasherTypes { + enum SlashingStatus { + Null, + Requested, + Completed, + Cancelled + } + + struct SlashingRequest { + IAllocationManager.SlashingParams params; + uint256 requestTimestamp; + SlashingStatus status; + } + +} + +interface ISlasher is ISlasherEvents, ISlasherTypes, ISlasherErrors {} diff --git a/src/interfaces/ISocketUpdater.sol b/src/interfaces/ISocketUpdater.sol index dcf5a865..dc17ecfa 100644 --- a/src/interfaces/ISocketUpdater.sol +++ b/src/interfaces/ISocketUpdater.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Interface for an `ISocketUpdater` where operators can update their sockets. @@ -11,7 +11,7 @@ interface ISocketUpdater { event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); // FUNCTIONS - + /** * @notice Updates the socket of the msg.sender given they are a registered operator * @param socket is the new socket of the operator diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 044ccd58..d56c01c1 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -1,17 +1,47 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IRegistry} from "./IRegistry.sol"; +enum StakeType { + TOTAL_DELEGATED, + TOTAL_SLASHABLE +} + +interface IStakeRegistryErrors { + /// @dev Thrown when the caller is not the registry coordinator + error OnlyRegistryCoordinator(); + /// @dev Thrown when the caller is not the owner of the registry coordinator + error OnlyRegistryCoordinatorOwner(); + /// @dev Thrown when the stake is below the minimum required for a quorum + error BelowMinimumStakeRequirement(); + /// @dev Thrown when a quorum being created already exists. + error QuorumAlreadyExists(); + /// @dev Thrown when a quorum does not exist. + error QuorumDoesNotExist(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when input arrays length is zero. + error InputArrayLengthZero(); + /// @dev Thrown when a duplicate strategy is provided in input array. + error InputDuplicateStrategy(); + /// @dev Thrown when multiplier input is zero. + error InputMultiplierZero(); + /// @dev Thrown when the blocknumber provided is not >= the provided StakeUpdate's updateBlockNumber + /// or if the blocknumber provided is not the nextUpdateBlockNumber + error InvalidBlockNumber(); + /// @dev Thrown when the quorum has no stake history at block number provided. + error EmptyStakeHistory(); +} + /** * @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums. * @author Layr Labs, Inc. */ -interface IStakeRegistry is IRegistry { - +interface IStakeRegistry is IRegistry, IStakeRegistryErrors { // DATA STRUCTURES /// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage @@ -42,6 +72,12 @@ interface IStakeRegistry is IRegistry { uint8 quorumNumber, uint96 stake ); + + /// @notice emitted when the look ahead time for checking operator shares is updated + event LookAheadPeriodChanged(uint32 oldLookAheadDays, uint32 newLookAheadDays); + + /// @notice emitted when the stake type is updated + event StakeTypeSet(StakeType newStakeType); /// @notice emitted when the minimum stake for a quorum is updated event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake); /// @notice emitted when a new quorum is created @@ -67,8 +103,8 @@ interface IStakeRegistry is IRegistry { * 4) the operator is not already registered */ function registerOperator( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes memory quorumNumbers ) external returns (uint96[] memory, uint96[] memory); @@ -89,7 +125,20 @@ interface IStakeRegistry is IRegistry { /** * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake */ - function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory strategyParams) external; + /// @notice Initialize a new quorum and push its first history update + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory _strategyParams + ) external; + + /// @notice Initialize a new quorum and push its first history update + function initializeSlashableStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + uint32 lookAheadPeriod, + StrategyParams[] memory _strategyParams + ) external; /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. function addStrategies( @@ -187,7 +236,7 @@ interface IStakeRegistry is IRegistry { /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the - * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry + * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry * corresponds to the operator's stake at `blockNumber`. Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param operatorId The id of the operator of interest. @@ -202,8 +251,8 @@ interface IStakeRegistry is IRegistry { returns (uint96); /** - * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the - * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. + * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the + * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. * Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. @@ -241,8 +290,8 @@ interface IStakeRegistry is IRegistry { * and should be deregistered. */ function updateOperatorStake( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes calldata quorumNumbers ) external returns (uint192); } diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol index 0d8adb92..dcda7a31 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -19,7 +19,7 @@ // The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for operations on the BN254 elliptic curve. @@ -46,6 +46,17 @@ library BN254 { uint256[2] Y; } + /// @dev Thrown when the sum of two points of G1 fails + error ECAddFailed(); + /// @dev Thrown when the scalar multiplication of a point of G1 fails + error ECMulFailed(); + /// @dev Thrown when the scalar is too large. + error ScalarTooLarge(); + /// @dev Thrown when the pairing check fails + error ECPairingFailed(); + /// @dev Thrown when the exponentiation mod fails + error ExpModFailed(); + function generatorG1() internal pure returns (G1Point memory) { return G1Point(1, 2); } @@ -114,7 +125,7 @@ library BN254 { } } - require(success, "ec-add-failed"); + require(success, ECAddFailed()); } /** @@ -122,9 +133,9 @@ library BN254 { * @param p the point to multiply * @param s the scalar to multiply by * @dev this function is only safe to use if the scalar is 9 bits or less - */ + */ function scalar_mul_tiny(BN254.G1Point memory p, uint16 s) internal view returns (BN254.G1Point memory) { - require(s < 2**9, "scalar-too-large"); + require(s < 2**9, ScalarTooLarge()); // if s is 1 return p if(s == 1) { @@ -155,7 +166,7 @@ library BN254 { ++i; } } - + // return the accumulated product return acc; } @@ -180,7 +191,7 @@ library BN254 { invalid() } } - require(success, "ec-mul-failed"); + require(success, ECMulFailed()); } /** @@ -223,7 +234,7 @@ library BN254 { } } - require(success, "pairing-opcode-failed"); + require(success, ECPairingFailed()); return out[0] != 0; } @@ -344,7 +355,7 @@ library BN254 { invalid() } } - require(success, "BN254.expMod: call failure"); + require(success, ExpModFailed()); return output[0]; } } diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index 41f9ef48..3930209a 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -1,12 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for Bitmap utilities such as converting between an array of bytes and a bitmap and finding the number of 1s in a bitmap. * @author Layr Labs, Inc. */ library BitmapUtils { + /// @dev Thrown when the input byte array is too long to be converted to a bitmap + error BytesArrayLengthTooLong(); + /// @dev Thrown when the input byte array is not strictly ordered + error BytesArrayNotOrdered(); + /// @dev Thrown when the bitmap value is too large + error BitmapValueTooLarge(); + /** * @notice Byte arrays are meant to contain unique bytes. * If the array length exceeds 256, then it's impossible for all entries to be unique. @@ -24,8 +31,7 @@ library BitmapUtils { */ function orderedBytesArrayToBitmap(bytes memory orderedBytesArray) internal pure returns (uint256) { // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s) - require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH, - "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is too long"); + require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH, BytesArrayLengthTooLong()); // return empty bitmap early if length of array is 0 if (orderedBytesArray.length == 0) { @@ -46,7 +52,7 @@ library BitmapUtils { // construct a single-bit mask from the numerical value of the next byte of the array bitMask = uint256(1 << uint8(orderedBytesArray[i])); // check strictly ascending array ordering by comparing the mask to the bitmap so far (revert if mask isn't greater than bitmap) - require(bitMask > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered"); + require(bitMask > bitmap, BytesArrayNotOrdered()); // add the entry to the bitmap bitmap = (bitmap | bitMask); } @@ -62,9 +68,7 @@ library BitmapUtils { function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 bitUpperBound) internal pure returns (uint256) { uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray); - require((1 << bitUpperBound) > bitmap, - "BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value" - ); + require((1 << bitUpperBound) > bitmap, BitmapValueTooLarge()); return bitmap; } @@ -95,11 +99,11 @@ library BitmapUtils { if (uint256(uint8(bytesArray[i])) <= uint256(uint8(singleByte))) { return false; } - + // Pull the next byte out of the array singleByte = bytesArray[i]; } - + return true; } @@ -149,9 +153,9 @@ library BitmapUtils { function isSet(uint256 bitmap, uint8 bit) internal pure returns (bool) { return 1 == ((bitmap >> bit) & 1); } - + /** - * @notice Returns a copy of `bitmap` with `bit` set. + * @notice Returns a copy of `bitmap` with `bit` set. * @dev IMPORTANT: we're dealing with stack values here, so this doesn't modify * the original bitmap. Using this correctly requires an assignment statement: * `bitmap = bitmap.setBit(bit);` diff --git a/src/libraries/LibMergeSort.sol b/src/libraries/LibMergeSort.sol new file mode 100644 index 00000000..11eb91b1 --- /dev/null +++ b/src/libraries/LibMergeSort.sol @@ -0,0 +1,61 @@ + +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +library LibMergeSort { + function sort(address[] memory array) internal pure returns (address[] memory) { + if (array.length <= 1) { + return array; + } + + uint256 mid = array.length / 2; + address[] memory left = new address[](mid); + address[] memory right = new address[](array.length - mid); + + for (uint256 i = 0; i < mid; i++) { + left[i] = array[i]; + } + for (uint256 i = mid; i < array.length; i++) { + right[i - mid] = array[i]; + } + + return mergeSortArrays(sort(left), sort(right)); + } + function mergeSortArrays(address[] memory left, address[] memory right) internal pure returns (address[] memory) { + uint256 leftLength = left.length; + uint256 rightLength = right.length; + address[] memory merged = new address[](leftLength + rightLength); + + uint256 i = 0; // Index for left array + uint256 j = 0; // Index for right array + uint256 k = 0; // Index for merged array + + // Merge the two arrays into the merged array + while (i < leftLength && j < rightLength) { + if (left[i] < right[j]) { + merged[k++] = left[i++]; + } else if (left[i] > right[j]) { + merged[k++] = right[j++]; + } else { + merged[k++] = left[i++]; + j++; + } + } + + // Copy remaining elements of left, if any + while (i < leftLength) { + merged[k++] = left[i++]; + } + + // Copy remaining elements of right, if any + while (j < rightLength) { + merged[k++] = right[j++]; + } + + // Resize the merged array to remove unused space + assembly { mstore(merged, k) } + + return merged; + } + +} \ No newline at end of file diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol new file mode 100644 index 00000000..50f6880b --- /dev/null +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IRegistryCoordinator} from "../interfaces/IRegistryCoordinator.sol"; + +/// @title QuorumBitmapHistoryLib +/// @notice This library operates on the _operatorBitmapHistory in the RegistryCoordinator +library QuorumBitmapHistoryLib { + /// @dev Thrown when the quorum bitmap update is not found + error BitmapUpdateNotFound(); + /// @dev Thrown when quorum bitmap update is after the block number + error BitmapUpdateIsAfterBlockNumber(); + /// @dev Thrown when the next update block number is not 0 and strictly greater than blockNumber + error NextBitmapUpdateIsBeforeBlockNumber(); + + /// @notice Retrieves the index of the quorum bitmap update at or before the specified block number + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param blockNumber The block number to search for + /// @param operatorId The ID of the operator + /// @return index The index of the quorum bitmap update + function getQuorumBitmapIndexAtBlockNumber( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + uint32 blockNumber, + bytes32 operatorId + ) internal view returns (uint32 index) { + uint256 length = self[operatorId].length; + + // Traverse the operator's bitmap history in reverse, returning the first index + // corresponding to an update made before or at `blockNumber` + for (uint256 i = 0; i < length; i++) { + index = uint32(length - i - 1); + + if (self[operatorId][index].updateBlockNumber <= blockNumber) { + return index; + } + } + + revert( + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" + ); + } + + /// @notice Retrieves the current quorum bitmap for the given operator ID + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @return The current quorum bitmap + function currentOperatorBitmap( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId + ) internal view returns (uint192) { + uint256 historyLength = self[operatorId].length; + if (historyLength == 0) { + return 0; + } else { + return self[operatorId][historyLength - 1].quorumBitmap; + } + } + + /// @notice Retrieves the indices of the quorum bitmap updates for the given operator IDs at the specified block number + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param blockNumber The block number to search for + /// @param operatorIds The array of operator IDs + /// @return An array of indices corresponding to the quorum bitmap updates + function getQuorumBitmapIndicesAtBlockNumber( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + uint32 blockNumber, + bytes32[] memory operatorIds + ) internal view returns (uint32[] memory) { + uint32[] memory indices = new uint32[](operatorIds.length); + for (uint256 i = 0; i < operatorIds.length; i++) { + indices[i] = getQuorumBitmapIndexAtBlockNumber(self, blockNumber, operatorIds[i]); + } + return indices; + } + + /// @notice Retrieves the quorum bitmap for the given operator ID at the specified block number and index + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @param blockNumber The block number to validate against + /// @param index The index of the quorum bitmap update + /// @return The quorum bitmap at the specified index + function getQuorumBitmapAtBlockNumberByIndex( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId, + uint32 blockNumber, + uint256 index + ) internal view returns (uint192) { + IRegistryCoordinator.QuorumBitmapUpdate memory quorumBitmapUpdate = self[operatorId][index]; + + /** + * Validate that the update is valid for the given blockNumber: + * - blockNumber should be >= the update block number + * - the next update block number should be either 0 or strictly greater than blockNumber + */ + require( + blockNumber >= quorumBitmapUpdate.updateBlockNumber, + BitmapUpdateIsAfterBlockNumber() + ); + require( + quorumBitmapUpdate.nextUpdateBlockNumber == 0 + || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, + NextBitmapUpdateIsBeforeBlockNumber() + ); + + return quorumBitmapUpdate.quorumBitmap; + } + + /// @notice Updates the quorum bitmap for the given operator ID + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @param newBitmap The new quorum bitmap to set + function updateOperatorBitmap( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId, + uint192 newBitmap + ) internal { + uint256 historyLength = self[operatorId].length; + + if (historyLength == 0) { + // No prior bitmap history - push our first entry + self[operatorId].push( + IRegistryCoordinator.QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); + } else { + // We have prior history - fetch our last-recorded update + IRegistryCoordinator.QuorumBitmapUpdate storage lastUpdate = + self[operatorId][historyLength - 1]; + + /** + * If the last update was made in the current block, update the entry. + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (lastUpdate.updateBlockNumber == uint32(block.number)) { + lastUpdate.quorumBitmap = newBitmap; + } else { + lastUpdate.nextUpdateBlockNumber = uint32(block.number); + self[operatorId].push( + IRegistryCoordinator.QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); + } + } + } +} diff --git a/src/libraries/SignatureCheckerLib.sol b/src/libraries/SignatureCheckerLib.sol new file mode 100644 index 00000000..864e4afb --- /dev/null +++ b/src/libraries/SignatureCheckerLib.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; + +/** + * @title SignatureCheckerLib + * @dev This library wraps the EIP1271SignatureUtils library to provide an external function for signature validation. + * This approach helps in reducing the code size of the RegistryCoordinator contract by offloading the signature + * validation logic to this external library. + */ +library SignatureCheckerLib { + error InvalidSignature(); + + /** + * @notice Validates a signature using EIP-1271 standard. + * @param signer The address of the signer. + * @param digestHash The hash of the data that was signed. + * @param signature The signature to be validated. + */ + function isValidSignature( + address signer, + bytes32 digestHash, + bytes memory signature + ) internal view { + if (!SignatureCheckerUpgradeable.isValidSignatureNow(signer, digestHash, signature)) { + revert InvalidSignature(); + } + } +} diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol new file mode 100644 index 00000000..976ad896 --- /dev/null +++ b/src/slashers/InstantSlasher.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {SlasherBase} from "./base/SlasherBase.sol"; + +contract InstantSlasher is SlasherBase { + + function initialize(address _serviceManager, address _slasher) external initializer { + __SlasherBase_init(_serviceManager, _slasher); + } + + function fulfillSlashingRequest( + IAllocationManager.SlashingParams memory _slashingParams + ) external virtual onlySlasher { + uint256 requestId = nextRequestId++; + _fulfillSlashingRequest(requestId, _slashingParams); + } + + +} \ No newline at end of file diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol new file mode 100644 index 00000000..880b481f --- /dev/null +++ b/src/slashers/VetoableSlasher.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {SlasherBase} from "./base/SlasherBase.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +contract VetoableSlashing is SlasherBase { + uint256 public constant VETO_PERIOD = 3 days; + address public vetoCommittee; + + mapping(uint256 => SlashingRequest) public slashingRequests; + + modifier onlyVetoCommittee() { + _checkVetoCommittee(msg.sender); + _; + } + + function initialize( + address _serviceManager, + address _vetoCommittee, + address _slasher + ) external virtual initializer { + __SlasherBase_init(_serviceManager, _slasher); + vetoCommittee = _vetoCommittee; + } + + function queueSlashingRequest(IAllocationManager.SlashingParams memory params) external virtual onlySlasher { + _queueSlashingRequest(params); + } + + function cancelSlashingRequest(uint256 requestId) external virtual onlyVetoCommittee { + require( + block.timestamp < slashingRequests[requestId].requestTimestamp + VETO_PERIOD, + VetoPeriodPassed() + ); + require(slashingRequests[requestId].status == SlashingStatus.Requested, SlashingRequestNotRequested()); + + _cancelSlashingRequest(requestId); + } + + function fulfillSlashingRequest(uint256 requestId) external virtual onlySlasher { + SlashingRequest storage request = slashingRequests[requestId]; + require(block.timestamp >= request.requestTimestamp + VETO_PERIOD, VetoPeriodNotPassed()); + require(request.status == SlashingStatus.Requested, SlashingRequestIsCancelled()); + + request.status = SlashingStatus.Completed; + + _fulfillSlashingRequest( + requestId, + request.params + ); + } + + function _queueSlashingRequest(IAllocationManager.SlashingParams memory params) internal virtual { + uint256 requestId = nextRequestId++; + slashingRequests[requestId] = SlashingRequest({ + params: params, + requestTimestamp: block.timestamp, + status: SlashingStatus.Requested + }); + + emit SlashingRequested(requestId, params.operator, params.operatorSetId, params.wadsToSlash, params.description); + } + + function _cancelSlashingRequest(uint256 requestId) internal virtual { + slashingRequests[requestId].status = SlashingStatus.Cancelled; + emit SlashingRequestCancelled(requestId); + } + + function _checkVetoCommittee(address account) internal view virtual { + require(account == vetoCommittee, OnlyVetoCommittee()); + } +} diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol new file mode 100644 index 00000000..f1d46870 --- /dev/null +++ b/src/slashers/base/SlasherBase.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {IServiceManager} from "../../interfaces/IServiceManager.sol"; +import {SlasherStorage} from "./SlasherStorage.sol"; +import {IAllocationManagerTypes, IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; + +abstract contract SlasherBase is Initializable, SlasherStorage { + + modifier onlySlasher() { + _checkSlasher(msg.sender); + _; + } + + function __SlasherBase_init(address _serviceManager, address _slasher) internal onlyInitializing { + serviceManager = _serviceManager; + slasher = _slasher; + } + + function _fulfillSlashingRequest( + uint256 _requestId, + IAllocationManager.SlashingParams memory _params + ) internal virtual { + IServiceManager(serviceManager).slashOperator(_params); + emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.wadsToSlash, _params.description); + } + + function _checkSlasher(address account) internal view virtual { + require(account == slasher, OnlySlasher()); + } +} + + + + diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol new file mode 100644 index 00000000..a3924f34 --- /dev/null +++ b/src/slashers/base/SlasherStorage.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {ISlasher} from "../../interfaces/ISlasher.sol"; +contract SlasherStorage is ISlasher { + address public serviceManager; + address public slasher; + uint256 public nextRequestId; + + uint256[47] private __gap; +} \ No newline at end of file diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 57326bea..e7b63d2c 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; @@ -12,6 +12,10 @@ import {IStakeRegistry} from "../interfaces/IStakeRegistry.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {Quorum} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; import {ECDSAStakeRegistry} from "../unaudited/ECDSAStakeRegistry.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + + abstract contract ECDSAServiceManagerBase is IServiceManager, @@ -23,6 +27,9 @@ abstract contract ECDSAServiceManagerBase is /// @notice Address of the AVS directory contract, which manages AVS-related data for registered operators. address public immutable avsDirectory; + /// @notice Address of the AllocationManager contract + address public immutable allocationManager; + /// @notice Address of the rewards coordinator contract, which handles rewards distributions. address internal immutable rewardsCoordinator; @@ -37,10 +44,7 @@ abstract contract ECDSAServiceManagerBase is * This is used to restrict certain registration and deregistration functionality to the `stakeRegistry` */ modifier onlyStakeRegistry() { - require( - msg.sender == stakeRegistry, - "ECDSAServiceManagerBase.onlyStakeRegistry: caller is not the stakeRegistry" - ); + require(msg.sender == stakeRegistry, OnlyStakeRegistry()); _; } @@ -53,10 +57,7 @@ abstract contract ECDSAServiceManagerBase is } function _checkRewardsInitiator() internal view { - require( - msg.sender == rewardsInitiator, - "ECDSAServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + require(msg.sender == rewardsInitiator, OnlyRewardsInitiator()); } /** @@ -70,12 +71,14 @@ abstract contract ECDSAServiceManagerBase is address _avsDirectory, address _stakeRegistry, address _rewardsCoordinator, - address _delegationManager + address _delegationManager, + address _allocationManager ) { avsDirectory = _avsDirectory; stakeRegistry = _stakeRegistry; rewardsCoordinator = _rewardsCoordinator; delegationManager = _delegationManager; + allocationManager = _allocationManager; _disableInitializers(); } @@ -198,9 +201,7 @@ abstract contract ECDSAServiceManagerBase is ); } - IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission( - rewardsSubmissions - ); + IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(rewardsSubmissions); } /** @@ -222,6 +223,15 @@ abstract contract ECDSAServiceManagerBase is return strategies; } + /** + * @notice Sets the AVS registrar address in the AllocationManager + * @param registrar The new AVS registrar address + * @dev Only callable by the registry coordinator + */ + function setAVSRegistrar(IAVSRegistrar registrar) external onlyOwner { + IAllocationManager(allocationManager).setAVSRegistrar(address(this), registrar); + } + /** * @notice Retrieves the addresses of strategies where the operator has restaked. * @dev This function fetches the quorum details from the ECDSAStakeRegistry, retrieves the operator's shares for each strategy, @@ -238,8 +248,7 @@ abstract contract ECDSAServiceManagerBase is for (uint256 i; i < count; i++) { strategies[i] = quorum.strategies[i].strategy; } - uint256[] memory shares = IDelegationManager(delegationManager) - .getOperatorShares(_operator, strategies); + uint256[] memory shares = IDelegationManager(delegationManager).getOperatorShares(_operator, strategies); uint256 activeCount; for (uint256 i; i < count; i++) { diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index ab4bdbeb..0e341067 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ECDSAStakeRegistryStorage, Quorum, StrategyParams} from "./ECDSAStakeRegistryStorage.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; @@ -248,7 +248,7 @@ contract ECDSAStakeRegistry is for (uint256 i; i < strategyParams.length; i++) { strategies[i] = strategyParams[i].strategy; } - uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares( + uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares( _operator, strategies ); diff --git a/src/unaudited/ECDSAStakeRegistryStorage.sol b/src/unaudited/ECDSAStakeRegistryStorage.sol index 0742157a..6509f214 100644 --- a/src/unaudited/ECDSAStakeRegistryStorage.sol +++ b/src/unaudited/ECDSAStakeRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; diff --git a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol index 23b8be2a..adfd2751 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistryPermissioned} from "./ECDSAStakeRegistryPermissioned.sol"; diff --git a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol index ef2e691c..ab0bca02 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistry} from "../ECDSAStakeRegistry.sol"; diff --git a/test/events/IBLSApkRegistryEvents.sol b/test/events/IBLSApkRegistryEvents.sol index 1ed588de..6921203d 100644 --- a/test/events/IBLSApkRegistryEvents.sol +++ b/test/events/IBLSApkRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BN254} from "../../src/libraries/BN254.sol"; @@ -17,7 +17,7 @@ interface IBLSApkRegistryEvents { // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); diff --git a/test/events/IIndexRegistryEvents.sol b/test/events/IIndexRegistryEvents.sol index 79494de0..21a0f0f2 100644 --- a/test/events/IIndexRegistryEvents.sol +++ b/test/events/IIndexRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; interface IIndexRegistryEvents { // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated diff --git a/test/events/IServiceManagerBaseEvents.sol b/test/events/IServiceManagerBaseEvents.sol index 6defff0d..1efb8fa0 100644 --- a/test/events/IServiceManagerBaseEvents.sol +++ b/test/events/IServiceManagerBaseEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import { IRewardsCoordinator, diff --git a/test/events/IStakeRegistryEvents.sol b/test/events/IStakeRegistryEvents.sol index 3e6f7e24..1b585ceb 100644 --- a/test/events/IStakeRegistryEvents.sol +++ b/test/events/IStakeRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStakeRegistry, IStrategy} from "src/interfaces/IStakeRegistry.sol"; diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 18d49a17..4f3906ba 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; import "../ffi/util/G2Operations.sol"; diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index 16f72b4e..16c22ebe 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {G2Operations} from "../ffi/util/G2Operations.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; @@ -30,13 +30,13 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and // the signature is only checked for stakes on that quorum - function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); uint256 gasBefore = gasleft(); @@ -44,9 +44,9 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, /* bytes32 signatoryRecordHash */ ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -61,14 +61,14 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums // and the signature is only checked for stakes on those quorums - function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); // 100 set bits uint256 quorumBitmap = (1 << 100) - 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); @@ -76,9 +76,9 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { uint256 gasBefore = gasleft(); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -168,8 +168,8 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, - referenceBlockNumber, - quorumNumbers, + referenceBlockNumber, + quorumNumbers, nonSignerOperatorIds ); diff --git a/test/ffi/UpdateOperators.t.sol b/test/ffi/UpdateOperators.t.sol index 26fad27d..64e3ecbc 100644 --- a/test/ffi/UpdateOperators.t.sol +++ b/test/ffi/UpdateOperators.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -37,7 +37,7 @@ contract Integration_AVS_Sync_GasCosts_FFI is IntegrationChecks { config_data, string.concat(".G1y[", vm.toString(i), "]") ); - // G2 + // G2 pubkey.pubkeyG2.X[1] = stdJson.readUint( config_data, string.concat(".G2x1[", vm.toString(i), "]") diff --git a/test/ffi/util/G2Operations.sol b/test/ffi/util/G2Operations.sol index f25cbbb7..016011da 100644 --- a/test/ffi/util/G2Operations.sol +++ b/test/ffi/util/G2Operations.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "openzeppelin-contracts/contracts/utils/Strings.sol"; @@ -13,7 +13,7 @@ contract G2Operations is Test { inputs[0] = "go"; inputs[1] = "run"; inputs[2] = "test/ffi/go/g2mul.go"; - inputs[3] = x.toString(); + inputs[3] = x.toString(); inputs[4] = "1"; bytes memory res = vm.ffi(inputs); diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol new file mode 100644 index 00000000..995f62a6 --- /dev/null +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; + +// wrapper around the AVSDirectory contract that exposes internal functionality, for unit testing +contract AVSDirectoryHarness is AVSDirectory { + constructor(IDelegationManager _dm, IPauserRegistry _pauser)AVSDirectory(_dm, _pauser){} +} \ No newline at end of file diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol index 546d355c..7cc8ec60 100644 --- a/test/harnesses/BLSApkRegistryHarness.sol +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol index 19e1135e..95322d21 100644 --- a/test/harnesses/BitmapUtilsWrapper.sol +++ b/test/harnesses/BitmapUtilsWrapper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BitmapUtils.sol"; diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index d7ae81ae..860e70dc 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/RegistryCoordinator.sol"; @@ -11,8 +11,9 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry - ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) { + IIndexRegistry _indexRegistry, + IPauserRegistry _pauserRegistry + ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _pauserRegistry) { _transferOwnership(msg.sender); } @@ -26,7 +27,7 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { // @notice exposes the internal `_registerOperator` function, overriding all access controls function _registerOperatorExternal( - address operator, + address operator, bytes32 operatorId, bytes calldata quorumNumbers, string memory socket, @@ -37,7 +38,7 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { // @notice exposes the internal `_deregisterOperator` function, overriding all access controls function _deregisterOperatorExternal( - address operator, + address operator, bytes calldata quorumNumbers ) external { _deregisterOperator(operator, quorumNumbers); diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 386508d9..7d00b107 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/StakeRegistry.sol"; @@ -7,8 +7,10 @@ import "../../src/StakeRegistry.sol"; contract StakeRegistryHarness is StakeRegistry { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager - ) StakeRegistry(_registryCoordinator, _delegationManager) { + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IServiceManager _serviceManager + ) StakeRegistry(_registryCoordinator, _delegationManager, _avsDirectory, _serviceManager) { } function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, uint96 newStake) external returns(int256) { diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 5998025c..4fbfc3ad 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -1,13 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; -import { IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { IAVSDirectory, IAVSDirectoryTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { IStrategyManager } from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; -import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import { IDelegationManager, IDelegationManagerTypes } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import { RewardsCoordinator } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import { PermissionController } from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; contract Test_CoreRegistration is MockAVSDeployer { // Contracts @@ -26,7 +28,15 @@ contract Test_CoreRegistration is MockAVSDeployer { _deployMockEigenLayerAndAVS(); // Deploy New DelegationManager - DelegationManager delegationManagerImplementation = new DelegationManager(strategyManagerMock, slasher, eigenPodManagerMock); + PermissionController permissionController; // TODO: Fix + DelegationManager delegationManagerImplementation = new DelegationManager( + IStrategyManager(address(strategyManagerMock)), + eigenPodManagerMock, + allocationManagerMock, + pauserRegistry, + permissionController, + 0 + ); IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); uint256[] memory initializeWithdrawalDelayBlocks = new uint256[](0); delegationManager = DelegationManager( @@ -48,7 +58,7 @@ contract Test_CoreRegistration is MockAVSDeployer { ); // Deploy New AVS Directory - AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager); + AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager, pauserRegistry); // TODO: Fix Config avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -72,14 +82,16 @@ contract Test_CoreRegistration is MockAVSDeployer { avsDirectory, rewardsCoordinatorMock, registryCoordinator, - stakeRegistry + stakeRegistry, + allocationManager ); registryCoordinatorImplementation = new RegistryCoordinatorHarness( serviceManager, stakeRegistry, blsApkRegistry, - indexRegistry + indexRegistry, + pauserRegistry ); // Upgrade Registry Coordinator & ServiceManager @@ -101,11 +113,9 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator to EigenLayer cheats.prank(operator); delegationManager.registerAsOperator( - IDelegationManager.OperatorDetails({ - __deprecated_earningsReceiver: operator, - delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 - }), + operator, + // TODO: fix or parameterize + 0, emptyStringForMetadataURI ); @@ -136,8 +146,8 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); // Check operator is registered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq(uint8(operatorStatus), uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED)); } function test_deregisterOperator_coreStateChanges() public { @@ -150,8 +160,8 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is deregistered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq(uint8(operatorStatus), uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.UNREGISTERED)); } function test_deregisterOperator_notGloballyDeregistered() public { @@ -166,8 +176,8 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is still registered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq(uint8(operatorStatus), uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED)); } function test_setMetadataURI_fail_notServiceManagerOwner() public { diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index c9695d70..0baf32b4 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; @@ -164,17 +164,17 @@ abstract contract IntegrationBase is IntegrationConfig { } /// AVSDirectory: - + function assert_NotRegisteredToAVS(User operator, string memory err) internal { - IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); - assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED, err); + assertTrue(status == IAVSDirectoryTypes.OperatorAVSRegistrationStatus.UNREGISTERED, err); } function assert_IsRegisteredToAVS(User operator, string memory err) internal { IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); - assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED, err); + assertTrue(status == IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED, err); } /******************************************************************************* @@ -222,7 +222,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Unchanged_QuorumBitmap(User user, string memory err) internal { bytes32 operatorId = user.operatorId(); - + uint192 curBitmap = _getQuorumBitmap(operatorId); uint192 prevBitmap = _getPrevQuorumBitmap(operatorId); @@ -288,11 +288,11 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < churnedQuorums.length; i++) { BN254.G1Point memory churnedPubkey = churnedOperators[i].pubkeyG1(); - BN254.G1Point memory expectedApk + BN254.G1Point memory expectedApk = prevApks[i] .plus(churnedPubkey.negate()) .plus(incomingPubkey); - + assertEq(expectedApk.X, curApks[i].X, err); assertEq(expectedApk.Y, curApks[i].Y, err); } @@ -300,7 +300,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that specific weights were added to the operator and total stakes for each quorum function assert_Snap_AddedWeightToStakes( - User user, + User user, bytes memory quorums, uint96[] memory addedWeights, string memory err @@ -320,7 +320,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator's stake weight was added to the operator and total /// stakes for each quorum function assert_Snap_Added_OperatorWeight( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -362,7 +362,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Unchanged_OperatorStake( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -431,7 +431,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Removed_TotalStake( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -462,7 +462,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Added_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] + 1, err); } @@ -471,7 +471,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Reduced_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] - 1, err); } @@ -480,7 +480,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Unchanged_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i], err); } @@ -491,7 +491,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the current list, but not the previous list function assert_Snap_Added_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); @@ -510,7 +510,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the previous list, but not the current list function assert_Snap_Removed_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); @@ -541,12 +541,12 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Replaced_OperatorListEntries( User incomingOperator, User[] memory churnedOperators, - bytes memory churnedQuorums, + bytes memory churnedQuorums, string memory err ) internal { // Sanity check input lengths assertEq(churnedOperators.length, churnedQuorums.length, "assert_Snap_Replaced_OperatorListEntries: input length mismatch"); - + bytes32[][] memory curOperatorLists = _getOperatorLists(churnedQuorums); bytes32[][] memory prevOperatorLists = _getPrevOperatorLists(churnedQuorums); @@ -568,11 +568,11 @@ abstract contract IntegrationBase is IntegrationConfig { TIME TRAVELERS ONLY BEYOND THIS POINT *******************************************************************************/ - /// @dev Check that the operator has `addedShares` additional operator shares + /// @dev Check that the operator has `addedShares` additional operator shares // for each strategy since the last snapshot function assert_Snap_Added_OperatorShares( - User operator, - IStrategy[] memory strategies, + User operator, + IStrategy[] memory strategies, uint[] memory addedShares, string memory err ) internal { @@ -589,9 +589,9 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator has `removedShares` fewer operator shares /// for each strategy since the last snapshot function assert_Snap_Removed_OperatorShares( - User operator, - IStrategy[] memory strategies, - uint[] memory removedShares, + User operator, + IStrategy[] memory strategies, + uint256[] memory removedShares, string memory err ) internal { uint[] memory curShares = _getOperatorShares(operator, strategies); @@ -607,8 +607,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `addedShares` additional delegatable shares /// for each strategy since the last snapshot function assert_Snap_Added_StakerShares( - User staker, - IStrategy[] memory strategies, + User staker, + IStrategy[] memory strategies, uint[] memory addedShares, string memory err ) internal { @@ -625,8 +625,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `removedShares` fewer delegatable shares /// for each strategy since the last snapshot function assert_Snap_Removed_StakerShares( - User staker, - IStrategy[] memory strategies, + User staker, + IStrategy[] memory strategies, uint[] memory removedShares, string memory err ) internal { @@ -641,7 +641,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Added_QueuedWithdrawals( - User staker, + User staker, IDelegationManager.Withdrawal[] memory withdrawals, string memory err ) internal { @@ -653,7 +653,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Added_QueuedWithdrawal( - User staker, + User staker, string memory err ) internal { uint curQueuedWithdrawal = _getCumulativeWithdrawals(staker); @@ -741,7 +741,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get operator shares at the last snapshot function _getPrevOperatorShares( - User operator, + User operator, IStrategy[] memory strategies ) internal timewarp() returns (uint[] memory) { return _getOperatorShares(operator, strategies); @@ -760,7 +760,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get staker shares at the last snapshot function _getPrevStakerShares( - User staker, + User staker, IStrategy[] memory strategies ) internal timewarp() returns (uint[] memory) { return _getStakerShares(staker, strategies); @@ -773,7 +773,7 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < strategies.length; i++) { IStrategy strat = strategies[i]; - curShares[i] = strategyManager.stakerStrategyShares(address(staker), strat); + curShares[i] = strategyManager.stakerDepositShares(address(staker), strat); } return curShares; @@ -786,7 +786,7 @@ abstract contract IntegrationBase is IntegrationConfig { function _getCumulativeWithdrawals(User staker) internal view returns (uint) { return delegationManager.cumulativeWithdrawalsQueued(address(staker)); } - + /// RegistryCoordinator: function _getOperatorInfo(User user) internal view returns (IRegistryCoordinator.OperatorInfo memory) { @@ -880,7 +880,7 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < quorums.length; i++) { stakes[i] = stakeRegistry.getCurrentTotalStake(uint8(quorums[i])); } - + return stakes; } diff --git a/test/integration/IntegrationChecks.t.sol b/test/integration/IntegrationChecks.t.sol index b54f8568..0f2de79c 100644 --- a/test/integration/IntegrationChecks.t.sol +++ b/test/integration/IntegrationChecks.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/IntegrationBase.t.sol"; import "test/integration/User.t.sol"; /// @notice Contract that provides utility functions to reuse common test blocks & checks contract IntegrationChecks is IntegrationBase { - + using BitmapUtils for *; /******************************************************************************* @@ -25,11 +25,11 @@ contract IntegrationChecks is IntegrationBase { "operator already has bits in quorum bitmap"); // BLSApkRegistry - assert_NoRegisteredPubkey(operator, + assert_NoRegisteredPubkey(operator, "operator already has a registered pubkey"); // DelegationManager - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } @@ -44,7 +44,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_Register_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should have operatorId"); assert_HasRegisteredStatus(operator, "operatorInfo status should be REGISTERED"); @@ -54,11 +54,11 @@ contract IntegrationChecks is IntegrationBase { "operator did not register for all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should have registered a pubkey"); - assert_Snap_Added_QuorumApk(operator, quorums, + assert_Snap_Added_QuorumApk(operator, quorums, "operator pubkey should have been added to each quorum apk"); - + // StakeRegistry assert_HasAtLeastMinimumStake(operator, quorums, "operator should have at least the minimum stake in each quorum"); @@ -86,14 +86,14 @@ contract IntegrationChecks is IntegrationBase { ) internal { _log("check_Churned_State", incomingOperator); - bytes memory combinedQuorums = + bytes memory combinedQuorums = churnedQuorums .orderedBytesArrayToBitmap() .plus(standardQuorums.orderedBytesArrayToBitmap()) .bitmapToBytesArray(); // RegistryCoordinator - assert_HasOperatorInfoWithId(incomingOperator, + assert_HasOperatorInfoWithId(incomingOperator, "operatorInfo should have operatorId"); assert_HasRegisteredStatus(incomingOperator, "operatorInfo status should be REGISTERED"); @@ -103,13 +103,13 @@ contract IntegrationChecks is IntegrationBase { "operator did not register for all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(incomingOperator, + assert_HasRegisteredPubkey(incomingOperator, "operator should have registered a pubkey"); assert_Snap_Added_QuorumApk(incomingOperator, standardQuorums, "operator pubkey should have been added to standardQuorums apks"); assert_Snap_Churned_QuorumApk(incomingOperator, churnedOperators, churnedQuorums, "operator pubkey should have been added and churned operator pubkeys should have been removed from apks"); - + // StakeRegistry assert_HasAtLeastMinimumStake(incomingOperator, combinedQuorums, "operator should have at least the minimum stake in each quorum"); @@ -140,7 +140,7 @@ contract IntegrationChecks is IntegrationBase { churnedQuorum[0] = churnedQuorums[i]; // RegistryCoordinator - assert_HasOperatorInfoWithId(churnedOperator, + assert_HasOperatorInfoWithId(churnedOperator, "churned operatorInfo should still have operatorId"); assert_NotRegisteredForQuorums(churnedOperator, churnedQuorum, "churned operator bitmap should not include churned quorums"); @@ -148,7 +148,7 @@ contract IntegrationChecks is IntegrationBase { "churned operator did not deregister from churned quorum"); // BLSApkRegistry - assert_HasRegisteredPubkey(churnedOperator, + assert_HasRegisteredPubkey(churnedOperator, "churned operator should still have a registered pubkey"); // StakeRegistry @@ -177,7 +177,7 @@ contract IntegrationChecks is IntegrationBase { "operator info should not have changed"); assert_Snap_Unchanged_QuorumBitmap(operator, "operators quorum bitmap should not have changed"); - + // BLSApkRegistry assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); @@ -185,9 +185,9 @@ contract IntegrationChecks is IntegrationBase { // StakeRegistry assert_Snap_Increased_OperatorWeight(operator, quorums, "operator weight should not have decreased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry @@ -205,8 +205,8 @@ contract IntegrationChecks is IntegrationBase { /// NOTE: This method assumes (and checks) that the operator already /// met the minimum stake before stake was added. function check_DepositUpdate_State( - User operator, - bytes memory quorums, + User operator, + bytes memory quorums, uint96[] memory addedWeights ) internal { _log("check_DepositUpdate_State", operator); @@ -245,7 +245,7 @@ contract IntegrationChecks is IntegrationBase { User operator, bytes memory quorums, IStrategy[] memory strategies, - uint[] memory shares + uint256[] memory shares ) internal { _log("check_Withdraw_State", operator); @@ -254,7 +254,7 @@ contract IntegrationChecks is IntegrationBase { "operator info should not have changed"); assert_Snap_Unchanged_QuorumBitmap(operator, "operators quorum bitmap should not have changed"); - + // BLSApkRegistry assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); @@ -262,9 +262,9 @@ contract IntegrationChecks is IntegrationBase { // StakeRegistry assert_Snap_Decreased_OperatorWeight(operator, quorums, "operator weight should not have increased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry @@ -288,7 +288,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_WithdrawUpdate_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); @@ -298,9 +298,9 @@ contract IntegrationChecks is IntegrationBase { "operator did not deregister from all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, + assert_Snap_Removed_QuorumApk(operator, quorums, "operator pubkey should have been subtracted from each quorum apk"); // StakeRegistry @@ -316,7 +316,7 @@ contract IntegrationChecks is IntegrationBase { "operator list should have one fewer entry"); // AVSDirectory - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } @@ -364,7 +364,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_Deregister_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_NotRegisteredForQuorums(operator, quorums, "current operator bitmap should not include quorums"); @@ -372,9 +372,9 @@ contract IntegrationChecks is IntegrationBase { "operator did not deregister from all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, + assert_Snap_Removed_QuorumApk(operator, quorums, "operator pubkey should have been subtracted from each quorum apk"); // StakeRegistry @@ -399,13 +399,13 @@ contract IntegrationChecks is IntegrationBase { // RegistryCoordinator assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_HasDeregisteredStatus(operator, "operatorInfo status should be DEREGISTERED"); // AVSDirectory - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } diff --git a/test/integration/IntegrationConfig.t.sol b/test/integration/IntegrationConfig.t.sol index d1cd2ac6..f3231ac3 100644 --- a/test/integration/IntegrationConfig.t.sol +++ b/test/integration/IntegrationConfig.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; @@ -94,10 +94,10 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { * so this is the best way to speed things up when running multiple tests. */ constructor() { - for (uint i = 0; i < NUM_GENERATED_OPERATORS; i++) { + for (uint i = 0; i < NUM_GENERATED_OPERATORS; i++) { IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; uint privKey = uint(keccak256(abi.encodePacked(i + 1))); - + pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); pubkey.pubkeyG2 = G2Operations.mul(privKey); @@ -125,7 +125,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /** * @param _randomSeed Fuzz tests supply a random u24 as input - * @param _userTypes [DEFAULT | ALT_METHODS] - every time a user is generated, it will use these values + * @param _userTypes [DEFAULT | ALT_METHODS] - every time a user is generated, it will use these values * @param _quorumConfig Quorums that are created/initialized in this method will be configured according * to this struct. See `QuorumConfig` above for details on each parameter. */ @@ -175,7 +175,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { emit log_named_uint("- Minimum stake", minimumStake); cheats.prank(registryCoordinatorOwner); - registryCoordinator.createQuorum({ + registryCoordinator.createTotalDelegatedStakeQuorum({ operatorSetParams: operatorSet, minimumStake: minimumStake, strategyParams: strategyParams @@ -211,7 +211,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { numOperators++; (User operator, IStrategy[] memory strategies, uint[] memory tokenBalances) = _randUser(operatorName); - + operator.registerAsOperator(); operator.depositIntoEigenlayer(strategies, tokenBalances); @@ -246,7 +246,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { IStrategy[] memory strategies = new IStrategy[](allStrats.length); uint[] memory balances = new uint[](allStrats.length); emit log_named_string("_dealRandTokens: dealing assets to", user.NAME()); - + // Deal the user a random balance between [MIN_BALANCE, MAX_BALANCE] for each existing strategy for (uint i = 0; i < allStrats.length; i++) { IStrategy strat = allStrats[i]; @@ -266,7 +266,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { IStrategy[] memory strategies = new IStrategy[](allStrats.length); uint[] memory balances = new uint[](allStrats.length); emit log_named_string("_dealMaxTokens: dealing assets to", user.NAME()); - + // Deal the user the 100 * MAX_BALANCE for each existing strategy for (uint i = 0; i < allStrats.length; i++) { IStrategy strat = allStrats[i]; @@ -287,7 +287,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @param standardQuorums the quorums that we want to register for WITHOUT churn /// @return churnTargets: one churnable operator for each churnQuorum function _getChurnTargets( - User incomingOperator, + User incomingOperator, bytes memory churnQuorums, bytes memory standardQuorums ) internal returns (User[] memory) { @@ -304,7 +304,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { for (uint i = 0; i < churnQuorums.length; i++) { uint8 quorum = uint8(churnQuorums[i]); - IRegistryCoordinator.OperatorSetParam memory params + IRegistryCoordinator.OperatorSetParam memory params = registryCoordinator.getOperatorSetParams(quorum); // Sanity check - make sure we're at the operator cap @@ -337,7 +337,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// From RegistryCoordinator._individualKickThreshold function _individualKickThreshold( - uint96 operatorStake, + uint96 operatorStake, IRegistryCoordinator.OperatorSetParam memory setParams ) internal pure returns (uint96) { return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; @@ -345,7 +345,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// From RegistryCoordinator._totalKickThreshold function _totalKickThreshold( - uint96 totalStake, + uint96 totalStake, IRegistryCoordinator.OperatorSetParam memory setParams ) internal pure returns (uint96) { return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; @@ -380,7 +380,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { function _selectRandRegisteredOperator(uint8 quorum) internal returns (User) { uint32 curNumOperators = indexRegistry.totalOperatorsForQuorum(quorum); - + bytes32 randId = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorum, operatorIndex: uint32(_randUint({ min: 0, max: curNumOperators - 1 })) @@ -404,7 +404,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Uses `random` to return a random uint, with a range given by `min` and `max` (inclusive) /// @return `min` <= result <= `max` - function _randUint(uint min, uint max) internal returns (uint) { + function _randUint(uint min, uint max) internal returns (uint) { uint range = max - min + 1; // calculate the number of bits needed for the range @@ -459,7 +459,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Select a random value from `arr` and return it. Reverts if arr is empty function _randValue(bytes memory arr) internal returns (uint) { assertTrue(arr.length > 0, "_randValue: tried to select value from empty array"); - + uint idx = _randUint({ min: 0, max: arr.length - 1 }); return uint(uint8(arr[idx])); } @@ -470,7 +470,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// NOTE: This should only be used when initializing quorums for the first time (in _configRand) function _randQuorumCount() private returns (uint) { uint quorumFlag = _randValue(numQuorumFlags); - + if (quorumFlag == ONE) { return 1; } else if (quorumFlag == TWO) { @@ -520,7 +520,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { return params; } - /** + /** * @dev Uses _randFillType to determine how many operators to register for a quorum initially * @return The number of operators to register */ @@ -541,7 +541,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Select a random number of quorums to initialize function _randMinStake() private returns (uint96) { uint minStakeFlag = _randValue(minStakeFlags); - + if (minStakeFlag == NO_MINIMUM) { return 0; } else if (minStakeFlag == HAS_MINIMUM) { diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index dbe12a2c..dda8ce58 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; @@ -14,13 +14,14 @@ import "@openzeppelin/contracts/utils/Strings.sol"; // Core contracts import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; import "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; -import "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; // Middleware contracts @@ -51,10 +52,11 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { EigenPodManager eigenPodManager; RewardsCoordinator rewardsCoordinator; PauserRegistry pauserRegistry; - Slasher slasher; IBeacon eigenPodBeacon; EigenPod pod; ETHPOSDepositMock ethPOSDeposit; + AllocationManager allocationManager; + PermissionController permissionController; // Base strategy implementation in case we want to create more strategies later StrategyBase baseStrategyImplementation; @@ -93,10 +95,23 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { uint256 constant MAX_STRATEGY_COUNT = 32; // From StakeRegistry.MAX_WEIGHING_FUNCTION_LENGTH uint96 constant DEFAULT_STRATEGY_MULTIPLIER = 1e18; // RewardsCoordinator + // Config Variables + /// @notice intervals(epochs) are 1 weeks + uint32 CALCULATION_INTERVAL_SECONDS = 7 days; + + /// @notice Max duration is 5 epochs (2 weeks * 5 = 10 weeks in seconds) uint32 MAX_REWARDS_DURATION = 70 days; + + /// @notice Lower bound start range is ~3 months into the past, multiple of CALCULATION_INTERVAL_SECONDS uint32 MAX_RETROACTIVE_LENGTH = 84 days; + /// @notice Upper bound start range is ~1 month into the future, multiple of CALCULATION_INTERVAL_SECONDS uint32 MAX_FUTURE_LENGTH = 28 days; - uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_092_632; + /// @notice absolute min timestamp that a rewards can start at + uint32 GENESIS_REWARDS_TIMESTAMP = 1712188800; + /// @notice Equivalent to 100%, but in basis points. + uint16 internal constant ONE_HUNDRED_IN_BIPS = 10_000; + + uint32 defaultOperatorSplitBips = 1000; /// @notice Delay in timestamp before a posted root can be claimed against uint32 activationDelay = 7 days; /// @notice intervals(epochs) are 2 weeks @@ -131,24 +146,28 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - slasher = Slasher( + eigenPodManager = EigenPodManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - eigenPodManager = EigenPodManager( + avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - avsDirectory = AVSDirectory( + + allocationManager = AllocationManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - // RewardsCoordinator = RewardsCoordinator( - // address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) - // ); + + permissionController = PermissionController(address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), ""))); + + rewardsCoordinator = RewardsCoordinator( + address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + ); // Deploy EigenPod Contracts pod = new EigenPod( @@ -159,24 +178,39 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { eigenPodBeacon = new UpgradeableBeacon(address(pod)); + PermissionController permissionControllerImplementation = new PermissionController(); + // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs DelegationManager delegationImplementation = - new DelegationManager(strategyManager, slasher, eigenPodManager); + new DelegationManager(strategyManager, eigenPodManager, allocationManager, pauserRegistry, permissionController, 0); StrategyManager strategyManagerImplementation = - new StrategyManager(delegationManager, eigenPodManager, slasher); - Slasher slasherImplementation = new Slasher(strategyManager, delegationManager); + new StrategyManager(delegationManager, pauserRegistry); EigenPodManager eigenPodManagerImplementation = new EigenPodManager( - ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegationManager + ethPOSDeposit, eigenPodBeacon, delegationManager, pauserRegistry + ); + console.log("HERE Impl"); + AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager, pauserRegistry); + + RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( + delegationManager, + IStrategyManager(address(strategyManager)), + allocationManager, + pauserRegistry, + permissionController, + CALCULATION_INTERVAL_SECONDS, + MAX_REWARDS_DURATION, + MAX_RETROACTIVE_LENGTH, + MAX_FUTURE_LENGTH, + GENESIS_REWARDS_TIMESTAMP + ); + + AllocationManager allocationManagerImplementation = new AllocationManager( + delegationManager, + pauserRegistry, + permissionController, + uint32(7 days), // DEALLOCATION_DELAY + uint32(1 days) // ALLOCATION_CONFIGURATION_DELAY ); - AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager); - // RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( - // delegationManager, - // IStrategyManager(address(strategyManager)), - // MAX_REWARDS_DURATION, - // MAX_RETROACTIVE_LENGTH, - // MAX_FUTURE_LENGTH, - // GENESIS_REWARDS_TIMESTAMP - // ); // Third, upgrade the proxy contracts to point to the implementations uint256 minWithdrawalDelayBlocks = 7 days / 12 seconds; @@ -189,11 +223,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { abi.encodeWithSelector( DelegationManager.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, - 0, /* initialPausedStatus */ - minWithdrawalDelayBlocks, - initializeStrategiesToSetDelayBlocks, - initializeWithdrawalDelayBlocks + 0 /* initialPausedStatus */ ) ); // StrategyManager @@ -204,18 +234,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { StrategyManager.initialize.selector, eigenLayerReputedMultisig, //initialOwner eigenLayerReputedMultisig, //initial whitelister - pauserRegistry, - 0 // initialPausedStatus - ) - ); - // Slasher - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(slasher))), - address(slasherImplementation), - abi.encodeWithSelector( - Slasher.initialize.selector, - eigenLayerReputedMultisig, - pauserRegistry, 0 // initialPausedStatus ) ); @@ -226,39 +244,51 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { abi.encodeWithSelector( EigenPodManager.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, 0 // initialPausedStatus ) ); // AVSDirectory proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryImplemntation), + address(avsDirectoryImplementation), abi.encodeWithSelector( AVSDirectory.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, + // pauserRegistry, + 0 // initialPausedStatus + ) + ); + + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation) + ); + + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(rewardsCoordinator))), + address(rewardsCoordinatorImplementation), + abi.encodeWithSelector( + RewardsCoordinator.initialize.selector, + eigenLayerReputedMultisig, // initialOwner + 0, // initialPausedStatus + rewardsUpdater, + activationDelay, + defaultOperatorSplitBips // defaultSplitBips + ) + ); + + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(allocationManager))), + address(allocationManagerImplementation), + abi.encodeWithSelector( + AllocationManager.initialize.selector, + eigenLayerReputedMultisig, // initialOwner 0 // initialPausedStatus ) ); - // // RewardsCoordinator - // proxyAdmin.upgradeAndCall( - // TransparentUpgradeableProxy(payable(address(rewardsCoordinator))), - // address(rewardsCoordinatorImplementation), - // abi.encodeWithSelector( - // RewardsCoordinator.initialize.selector, - // eigenLayerReputedMultisig, // initialOwner - // pauserRegistry, - // 0, // initialPausedStatus - // rewardsUpdater, - // activationDelay, - // calculationIntervalSeconds, - // globalCommissionBips - // ) - // ); // Deploy and whitelist strategies - baseStrategyImplementation = new StrategyBase(strategyManager); + baseStrategyImplementation = new StrategyBase(strategyManager, pauserRegistry); for (uint256 i = 0; i < MAX_STRATEGY_COUNT; i++) { string memory number = uint256(i).toString(); string memory stratName = string.concat("StrategyToken", number); @@ -302,7 +332,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { cheats.stopPrank(); StakeRegistry stakeRegistryImplementation = new StakeRegistry( - IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager) + IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager), IAVSDirectory(avsDirectory), IServiceManager(serviceManager) ); BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(IRegistryCoordinator(registryCoordinator)); @@ -312,7 +342,8 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { IAVSDirectory(avsDirectory), rewardsCoordinator, IRegistryCoordinator(registryCoordinator), - stakeRegistry + stakeRegistry, + allocationManager ); proxyAdmin.upgrade( @@ -337,11 +368,15 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { serviceManager.initialize({ initialOwner: registryCoordinatorOwner, - rewardsInitiator: address(msg.sender) + rewardsInitiator: address(msg.sender), + slasher: address(msg.sender) }); + StakeType[] memory quorumStakeTypes = new StakeType[](0); + uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](0); + RegistryCoordinator registryCoordinatorImplementation = - new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry); + new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, pauserRegistry); proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), @@ -350,11 +385,12 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { registryCoordinatorOwner, churnApprover, ejector, - pauserRegistry, 0, /*initialPausedStatus*/ new IRegistryCoordinator.OperatorSetParam[](0), new uint96[](0), - new IStakeRegistry.StrategyParams[][](0) + new IStakeRegistry.StrategyParams[][](0), + quorumStakeTypes, + slashableStakeQuorumLookAheadPeriods ) ); @@ -389,7 +425,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { strategies[0] = strategy; cheats.prank(strategyManager.strategyWhitelister()); strategyManager.addStrategiesToDepositWhitelist( - strategies, thirdPartyTransfersForbiddenValues + strategies ); // Add to allStrats diff --git a/test/integration/TimeMachine.t.sol b/test/integration/TimeMachine.t.sol index b1df82d5..b1cffa68 100644 --- a/test/integration/TimeMachine.t.sol +++ b/test/integration/TimeMachine.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index 9e5e5990..23e9ffcc 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; @@ -11,6 +11,7 @@ import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; // Core import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; import "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; @@ -56,7 +57,7 @@ contract User is Test { BLSApkRegistry blsApkRegistry; StakeRegistry stakeRegistry; IndexRegistry indexRegistry; - + TimeMachine timeMachine; uint churnApproverPrivateKey; @@ -146,12 +147,12 @@ contract User is Test { assertEq(churnQuorums.length, churnTargets.length, "User.registerOperatorWithChurn: input length mismatch"); assertTrue(churnBitmap.noBitsInCommon(standardBitmap), "User.registerOperatorWithChurn: input quorums have common bits"); - bytes memory allQuorums = + bytes memory allQuorums = churnBitmap .plus(standardBitmap) .bitmapToBytesArray(); - IRegistryCoordinator.OperatorKickParam[] memory kickParams + IRegistryCoordinator.OperatorKickParam[] memory kickParams = new IRegistryCoordinator.OperatorKickParam[](allQuorums.length); // this constructs OperatorKickParam[] in ascending quorum order @@ -243,13 +244,8 @@ contract User is Test { function registerAsOperator() public createSnapshot virtual { _log("registerAsOperator (core)"); - IDelegationManager.OperatorDetails memory details = IDelegationManager.OperatorDetails({ - __deprecated_earningsReceiver: address(this), - delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 - }); - - delegationManager.registerAsOperator(details, NAME); + /// TODO: check + delegationManager.registerAsOperator(msg.sender,0, NAME); } // Deposit LSTs into the StrategyManager. This setup does not use the EPMgr or native ETH. @@ -266,16 +262,16 @@ contract User is Test { } } - function exitEigenlayer() public createSnapshot virtual returns (IStrategy[] memory, uint[] memory) { + function exitEigenlayer() public createSnapshot virtual returns (IStrategy[] memory, uint256[] memory) { _log("exitEigenlayer (core)"); - (IStrategy[] memory strategies, uint[] memory shares) = delegationManager.getDelegatableShares(address(this)); + (IStrategy[] memory strategies, uint256[] memory shares) = delegationManager.getDepositedShares(address(this)); - IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - params[0] = IDelegationManager.QueuedWithdrawalParams({ + IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); + params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ strategies: strategies, - shares: shares, - withdrawer: address(this) + depositShares: shares, + __deprecated_withdrawer: address(this) }); delegationManager.queueWithdrawals(params); @@ -286,7 +282,6 @@ contract User is Test { /** * EIP1271 Signatures: */ - bytes4 internal constant EIP1271_MAGICVALUE = 0x1626ba7e; function isValidSignature(bytes32 digestHash, bytes memory) public view returns (bytes4) { @@ -329,14 +324,14 @@ contract User is Test { emit log_named_string(string.concat(NAME, ".", s), quorums.toString()); } - // Operator0.registerOperatorWithChurn + // Operator0.registerOperatorWithChurn // - standardQuorums: 0x00010203... // - churnQuorums: 0x0405... // - churnTargets: Operator1, Operator2, ... function _logChurn( - string memory s, - bytes memory churnQuorums, - User[] memory churnTargets, + string memory s, + bytes memory churnQuorums, + User[] memory churnTargets, bytes memory standardQuorums ) internal virtual { emit log(string.concat(NAME, ".", s)); @@ -368,7 +363,7 @@ contract User_AltMethods is User { _; } - constructor(string memory name, uint _privKey, IBLSApkRegistry.PubkeyRegistrationParams memory _pubkeyParams) + constructor(string memory name, uint _privKey, IBLSApkRegistry.PubkeyRegistrationParams memory _pubkeyParams) User(name, _privKey, _pubkeyParams) {} /// @dev Rather than calling deregisterOperator, this pranks the ejector and calls @@ -402,6 +397,6 @@ contract User_AltMethods is User { operatorsPerQuorum[i] = Sort.sortAddresses(operatorsPerQuorum[i]); } - registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, allQuorums); + registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, allQuorums); } } diff --git a/test/integration/mocks/BeaconChainOracleMock.t.sol b/test/integration/mocks/BeaconChainOracleMock.t.sol new file mode 100644 index 00000000..5c2e4b77 --- /dev/null +++ b/test/integration/mocks/BeaconChainOracleMock.t.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +// import "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; + +// NOTE: There's a copy of this file in the core repo, but importing that was causing +// the compiler to complain for an unfathomable reason. Apparently reimplementing it +// here fixes the issue. +contract BeaconChainOracleMock { + // contract BeaconChainOracleMock is IBeaconChainOracle { + + mapping(uint64 => bytes32) blockRoots; + + function timestampToBlockRoot(uint256 timestamp) public view returns (bytes32) { + return blockRoots[uint64(timestamp)]; + } + + function setBlockRoot(uint64 timestamp, bytes32 blockRoot) public { + blockRoots[timestamp] = blockRoot; + } +} + diff --git a/test/integration/tests/Full_Register_Deregister.t.sol b/test/integration/tests/Full_Register_Deregister.t.sol index 48bb150a..2561aa2c 100644 --- a/test/integration/tests/Full_Register_Deregister.t.sol +++ b/test/integration/tests/Full_Register_Deregister.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -109,7 +109,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { churnTarget.registerOperator(quorum); check_Register_State(churnTarget, quorum); } - + // 4. Original operator re-registers for all quorums by churning old operators again operator.registerOperatorWithChurn(quorums, churnTargets, new bytes(0)); check_Churned_State({ @@ -140,7 +140,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { // Select some quorums to register using churn, and the rest without churn bytes memory churnQuorums = _selectRand(quorums); - bytes memory standardQuorums = + bytes memory standardQuorums = quorums .orderedBytesArrayToBitmap() .minus(churnQuorums.orderedBytesArrayToBitmap()) diff --git a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol index 43daf518..d9b74e8d 100644 --- a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol +++ b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -118,7 +118,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // 2. (core) queue full withdrawal - (IStrategy[] memory strategies, uint[] memory shares) = operator.exitEigenlayer(); + (IStrategy[] memory strategies, uint256[] memory shares) = operator.exitEigenlayer(); check_Withdraw_State(operator, quorums, strategies, shares); // 3. Update stakes @@ -151,7 +151,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // 2. (core) queue full withdrawal - (IStrategy[] memory strategies, uint[] memory shares) = operator.exitEigenlayer(); + (IStrategy[] memory strategies, uint256[] memory shares) = operator.exitEigenlayer(); check_Withdraw_State(operator, quorums, strategies, shares); // 3. Deregister from all quorums diff --git a/test/integration/tests/NonFull_Register_Deregister.t.sol b/test/integration/tests/NonFull_Register_Deregister.t.sol index c30c5261..6acf85cb 100644 --- a/test/integration/tests/NonFull_Register_Deregister.t.sol +++ b/test/integration/tests/NonFull_Register_Deregister.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; diff --git a/test/integration/utils/BitmapStrings.t.sol b/test/integration/utils/BitmapStrings.t.sol index 532f5d8d..2bcec384 100644 --- a/test/integration/utils/BitmapStrings.t.sol +++ b/test/integration/utils/BitmapStrings.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/Strings.sol"; @@ -16,12 +16,12 @@ library BitmapStrings { for (uint i = 0; i < bitmapArr.length; i++) { if (i == bitmapArr.length - 1) { result = string.concat( - result, + result, uint(uint8(bitmapArr[i])).toString() ); } else { result = string.concat( - result, + result, uint(uint8(bitmapArr[i])).toString(), ", " ); diff --git a/test/integration/utils/Sort.t.sol b/test/integration/utils/Sort.t.sol index 46a2fc2b..968400c6 100644 --- a/test/integration/utils/Sort.t.sol +++ b/test/integration/utils/Sort.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; library Sort { /** diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 813e913a..ca3613ef 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -1,52 +1,174 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IAVSDirectory, ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; contract AVSDirectoryMock is IAVSDirectory { - /** - * @notice Called by an avs to register an operator 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 - ) external {} - - /** - * @notice Called by an avs to deregister an operator with the avs. - * @param operator The address of the operator to deregister. - */ - function deregisterOperatorFromAVS(address operator) external {} - - /** - * @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated. - * @param metadataURI The URI for metadata associated with an AVS - * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `AVSMetadataURIUpdated` event - */ - function updateAVSMetadataURI(string calldata metadataURI) external {} - - /** - * @notice Returns whether or not the salt has already been used by the operator. - * @dev Salts is used in the `registerOperatorToAVS` function. - */ - function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool) {} - - /** - * @notice Calculates the digest hash to be signed by an operator to register with an AVS - * @param operator The account registering as an operator - * @param avs The AVS the operator is registering to - * @param salt A unique and single use value associated with the approver signature. - * @param expiry Time after which the approver's signature becomes invalid - */ - function calculateOperatorAVSRegistrationDigestHash( - address operator, - address avs, - bytes32 salt, - uint256 expiry - ) external view returns (bytes32) {} - - /// @notice The EIP-712 typehash for the Registration struct used by the contract - function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} -} + function initialize( + address initialOwner, + uint256 initialPausedStatus + ) external {} + + function createOperatorSets( + uint32[] calldata operatorSetIds + ) external {} + + function becomeOperatorSetAVS() external {} + + function migrateOperatorsToOperatorSets( + address[] calldata operators, + uint32[][] calldata operatorSetIds + ) external {} + + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function forceDeregisterFromOperatorSets( + address operator, + address avs, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] calldata operatorSetIds + ) external {} + + function addStrategiesToOperatorSet( + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external {} + + function removeStrategiesFromOperatorSet( + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external {} + + function updateAVSMetadataURI( + string calldata metadataURI + ) external {} + + function cancelSalt(bytes32 salt) external {} + + function registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function deregisterOperatorFromAVS(address operator) external {} + + function operatorSaltIsSpent( + address operator, + bytes32 salt + ) external view returns (bool) {} + + function isOperatorSetAVS( + address avs + ) external view returns (bool) {} + + function isOperatorSet( + address avs, + uint32 operatorSetId + ) external view returns (bool) {} + + function getNumOperatorSetsOfOperator( + address operator + ) external view returns (uint256) {} + + function inTotalOperatorSets( + address operator + ) external view returns (uint256) {} + + function calculateOperatorAVSRegistrationDigestHash( + address operator, + address avs, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + function calculateOperatorSetRegistrationDigestHash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + function calculateOperatorSetForceDeregistrationTypehash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + function OPERATOR_AVS_REGISTRATION_TYPEHASH() + external + view + + returns (bytes32) + {} + + function OPERATOR_SET_REGISTRATION_TYPEHASH() + external + view + + returns (bytes32) + {} + + function operatorSetStatus( + address avs, + address operator, + uint32 operatorSetId + ) + external + view + + returns (bool registered, uint32 lastDeregisteredTimestamp) + {} + + function initialize( + address initialOwner, + IPauserRegistry _pauserRegistry, + uint256 initialPausedStatus + ) external {} + + function operatorSetMemberAtIndex( + OperatorSet memory operatorSet, + uint256 index + ) external view returns (address) {} + + function getOperatorsInOperatorSet( + OperatorSet memory operatorSet, + uint256 start, + uint256 length + ) external view returns (address[] memory operators) {} + + function getStrategiesInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (IStrategy[] memory strategies) {} + + function getNumOperatorsInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (uint256) {} + + function isMember( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} + + function isOperatorSlashable( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} + + function isOperatorSetBatch( + OperatorSet[] calldata operatorSets + ) external view returns (bool) {} +} \ No newline at end of file diff --git a/test/mocks/AVSRegistrarMock.sol b/test/mocks/AVSRegistrarMock.sol new file mode 100644 index 00000000..ce4ce2bc --- /dev/null +++ b/test/mocks/AVSRegistrarMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {AVSRegistrar} from "../../src/AVSRegistrar.sol"; + +contract AVSRegistrarMock is AVSRegistrar { + function registerOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) external override {} + + function deregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) external override {} +} diff --git a/test/mocks/AllocationManagerMock.sol b/test/mocks/AllocationManagerMock.sol new file mode 100644 index 00000000..b355af95 --- /dev/null +++ b/test/mocks/AllocationManagerMock.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllocationManager, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAVSRegistrar } from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +contract AllocationManagerIntermediate is IAllocationManager { + function initialize( + address initialOwner, + uint256 initialPausedStatus + ) external virtual {} + + function slashOperator( + address avs, + SlashingParams calldata params + ) external virtual {} + + function modifyAllocations( + address operator, + AllocateParams[] calldata params + ) external virtual {} + + function clearDeallocationQueue( + address operator, + IStrategy[] calldata strategies, + uint16[] calldata numToClear + ) external virtual {} + + function registerForOperatorSets( + address operator, + RegisterParams calldata params + ) external virtual {} + + function deregisterFromOperatorSets( + DeregisterParams calldata params + ) external virtual {} + + function setAllocationDelay( + address operator, + uint32 delay + ) external virtual {} + + function setAVSRegistrar( + address avs, + IAVSRegistrar registrar + ) external virtual {} + + function updateAVSMetadataURI( + address avs, + string calldata metadataURI + ) external virtual {} + + function createOperatorSets( + address avs, + CreateSetParams[] calldata params + ) external virtual {} + + function addStrategiesToOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external virtual {} + + function removeStrategiesFromOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external virtual {} + + function getOperatorSetCount( + address avs + ) external view virtual returns (uint256) {} + + function getAllocatedSets( + address operator + ) external view virtual returns (OperatorSet[] memory) {} + + function getAllocatedStrategies( + address operator, + OperatorSet memory operatorSet + ) external view virtual returns (IStrategy[] memory) {} + + function getAllocation( + address operator, + OperatorSet memory operatorSet, + IStrategy strategy + ) external view virtual returns (Allocation memory) {} + + function getAllocations( + address[] memory operators, + OperatorSet memory operatorSet, + IStrategy strategy + ) external view virtual returns (Allocation[] memory) {} + + function getStrategyAllocations( + address operator, + IStrategy strategy + ) + external + view + virtual + returns (OperatorSet[] memory, Allocation[] memory) + {} + + function getAllocatableMagnitude( + address operator, + IStrategy strategy + ) external view virtual returns (uint64) {} + + function getMaxMagnitude( + address operator, + IStrategy strategy + ) external view virtual returns (uint64) {} + + function getMaxMagnitudes( + address operator, + IStrategy[] calldata strategies + ) external view virtual returns (uint64[] memory) {} + + function getMaxMagnitudes( + address[] calldata operators, + IStrategy strategy + ) external view virtual returns (uint64[] memory) {} + + function getMaxMagnitudesAtBlock( + address operator, + IStrategy[] calldata strategies, + uint32 blockNumber + ) external view virtual returns (uint64[] memory) {} + + function getAllocationDelay( + address operator + ) external view virtual returns (bool isSet, uint32 delay) {} + + function getRegisteredSets( + address operator + ) external view virtual returns (OperatorSet[] memory operatorSets) {} + + function isOperatorSet( + OperatorSet memory operatorSet + ) external view virtual returns (bool) {} + + function getMembers( + OperatorSet memory operatorSet + ) external view virtual returns (address[] memory operators) {} + + function getMemberCount( + OperatorSet memory operatorSet + ) external view virtual returns (uint256) {} + + function getAVSRegistrar( + address avs + ) external view virtual returns (IAVSRegistrar) {} + + function getStrategiesInOperatorSet( + OperatorSet memory operatorSet + ) external view virtual returns (IStrategy[] memory strategies) {} + + function getMinimumSlashableStake( + OperatorSet memory operatorSet, + address[] memory operators, + IStrategy[] memory strategies, + uint32 futureBlock + ) external view virtual returns (uint256[][] memory slashableStake) {} + + function isMemberOfOperatorSet( + address operator, + OperatorSet memory operatorSet + ) external view virtual returns (bool) {} +} + +contract AllocationManagerMock is AllocationManagerIntermediate { + +} \ No newline at end of file diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 88cd9d20..36f973da 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -1,203 +1,299 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {console2 as console} from "forge-std/Test.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {SlashingLib} from "eigenlayer-contracts/src/contracts/libraries/SlashingLib.sol"; + +contract DelegationIntermediate is IDelegationManager { + function initialize( + address initialOwner, + uint256 initialPausedStatus + ) external virtual {} + + function registerAsOperator( + OperatorDetails calldata registeringOperatorDetails, + uint32 allocationDelay, + string calldata metadataURI + ) external virtual {} + + function modifyOperatorDetails( + OperatorDetails calldata newOperatorDetails + ) external virtual {} + + function updateOperatorMetadataURI( + string calldata metadataURI + ) external virtual {} + + function delegateTo( + address operator, + SignatureWithExpiry memory approverSignatureAndExpiry, + bytes32 approverSalt + ) external virtual {} + + function undelegate( + address staker + ) external virtual returns (bytes32[] memory withdrawalRoots) {} + + function queueWithdrawals( + QueuedWithdrawalParams[] calldata params + ) external virtual returns (bytes32[] memory) {} + + function completeQueuedWithdrawals( + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens, + uint256 numToComplete + ) external virtual {} + + function completeQueuedWithdrawal( + Withdrawal calldata withdrawal, + IERC20[] calldata tokens, + bool receiveAsTokens + ) external virtual {} + + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens + ) external virtual {} + + function increaseDelegatedShares( + address staker, + IStrategy strategy, + uint256 existingDepositShares, + uint256 addedShares + ) external virtual {} + + function decreaseBeaconChainScalingFactor( + address staker, + uint256 existingShares, + uint64 proportionOfOldBalance + ) external virtual {} + + function burnOperatorShares( + address operator, + IStrategy strategy, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) external virtual {} + + function completeQueuedWithdrawal( + Withdrawal calldata withdrawal, + IERC20[] calldata tokens, + uint256 middlewareTimesIndex, + bool receiveAsTokens + ) external virtual {} + + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + uint256[] calldata middlewareTimesIndexes, + bool[] calldata receiveAsTokens + ) external virtual {} + + function delegatedTo( + address staker + ) external view virtual returns (address) {} + + function delegationApproverSaltIsSpent( + address _delegationApprover, + bytes32 salt + ) external view virtual returns (bool) {} + + function cumulativeWithdrawalsQueued( + address staker + ) external view virtual returns (uint256) {} + + function isDelegated(address staker) external view virtual returns (bool) {} + + function isOperator(address operator) external view virtual returns (bool) {} + + function operatorDetails( + address operator + ) external view virtual returns (OperatorDetails memory) {} + + function delegationApprover( + address operator + ) external view virtual returns (address) {} + + function getOperatorShares( + address operator, + IStrategy[] memory strategies + ) external view virtual returns (uint256[] memory) {} + + function getOperatorsShares( + address[] memory operators, + IStrategy[] memory strategies + ) external view virtual returns (uint256[][] memory) {} + + function getSlashableSharesInQueue( + address operator, + IStrategy strategy + ) external view virtual returns (uint256) {} + + function getWithdrawableShares( + address staker, + IStrategy[] memory strategies + ) + external + view + virtual + override + returns ( + uint256[] memory withdrawableShares, + uint256[] memory depositShares + ) + {} + + function getDepositedShares( + address staker + ) external view virtual returns (IStrategy[] memory, uint256[] memory) {} + + function depositScalingFactor( + address staker, + IStrategy strategy + ) external view virtual returns (uint256) {} + + function getBeaconChainSlashingFactor( + address staker + ) external view virtual returns (uint64) {} + + function getQueuedWithdrawals( + address staker + ) + external + view + virtual + override + returns (Withdrawal[] memory withdrawals, uint256[][] memory shares) + {} + + function calculateWithdrawalRoot( + Withdrawal memory withdrawal + ) external pure virtual returns (bytes32) {} + + function calculateDelegationApprovalDigestHash( + address staker, + address operator, + address _delegationApprover, + bytes32 approverSalt, + uint256 expiry + ) external view virtual returns (bytes32) {} + + function beaconChainETHStrategy() + external + view + virtual + override + returns (IStrategy) + {} + + function DELEGATION_APPROVAL_TYPEHASH() + external + view + virtual + override + returns (bytes32) + {} + + function registerAsOperator( + address initDelegationApprover, + uint32 allocationDelay, + string calldata metadataURI + ) external virtual {} + + function modifyOperatorDetails( + address operator, + address newDelegationApprover + ) external virtual {} + + function updateOperatorMetadataURI( + address operator, + string calldata metadataURI + ) external virtual {} + + function redelegate( + address newOperator, + SignatureWithExpiry memory newOperatorApproverSig, + bytes32 approverSalt + ) external virtual returns (bytes32[] memory withdrawalRoots) {} + + function decreaseDelegatedShares( + address staker, + uint256 curDepositShares, + uint64 prevBeaconChainSlashingFactor, + uint256 wadSlashed + ) external virtual {} + + function decreaseDelegatedShares( + address staker, + uint256 curDepositShares, + uint64 beaconChainSlashingFactorDecrease + ) external virtual {} + + function minWithdrawalDelayBlocks() + external + view + virtual + override + returns (uint32) + {} + + function slashOperatorShares( + address operator, + IStrategy strategy, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) external override {} + + function getQueuedWithdrawal( + bytes32 withdrawalRoot + ) external view override returns (Withdrawal memory) {} + + function getQueuedWithdrawalRoots( + address staker + ) external view override returns (bytes32[] memory) {} + + function convertToDepositShares( + address staker, + IStrategy[] memory strategies, + uint256[] memory withdrawableShares + ) external view override returns (uint256[] memory) {} +} -contract DelegationMock is IDelegationManager { - mapping(address => bool) public isOperator; - mapping(address => mapping(IStrategy => uint256)) public operatorShares; - - function setIsOperator(address operator, bool _isOperatorReturnValue) external { - isOperator[operator] = _isOperatorReturnValue; - } - - /// @notice returns the total number of shares in `strategy` that are delegated to `operator`. - function setOperatorShares(address operator, IStrategy strategy, uint256 shares) external { - operatorShares[operator][strategy] = shares; - } - - mapping (address => address) public delegatedTo; - - function registerAsOperator(OperatorDetails calldata /*registeringOperatorDetails*/, string calldata /*metadataURI*/) external pure {} - - function updateOperatorMetadataURI(string calldata /*metadataURI*/) external pure {} - - function updateAVSMetadataURI(string calldata /*metadataURI*/) external pure {} - - function delegateTo(address operator, SignatureWithExpiry memory /*approverSignatureAndExpiry*/, bytes32 /*approverSalt*/) external { - delegatedTo[msg.sender] = operator; - } - - function modifyOperatorDetails(OperatorDetails calldata /*newOperatorDetails*/) external pure {} - - function delegateToBySignature( - address /*staker*/, - address /*operator*/, - SignatureWithExpiry memory /*stakerSignatureAndExpiry*/, - SignatureWithExpiry memory /*approverSignatureAndExpiry*/, - bytes32 /*approverSalt*/ - ) external pure {} - - function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot) { - delegatedTo[staker] = address(0); - return withdrawalRoot; - } - - function increaseDelegatedShares(address /*staker*/, IStrategy /*strategy*/, uint256 /*shares*/) external pure {} - - function decreaseDelegatedShares( - address /*staker*/, - IStrategy /*strategy*/, - uint256 /*shares*/ - ) external pure {} - - function operatorDetails(address operator) external pure returns (OperatorDetails memory) { - OperatorDetails memory returnValue = OperatorDetails({ - __deprecated_earningsReceiver: operator, - delegationApprover: operator, - stakerOptOutWindowBlocks: 0 - }); - return returnValue; - } - - function beaconChainETHStrategy() external pure returns (IStrategy) {} - - function earningsReceiver(address operator) external pure returns (address) { - return operator; - } - - function delegationApprover(address operator) external pure returns (address) { - return operator; - } - - function stakerOptOutWindowBlocks(address /*operator*/) external pure returns (uint256) { - return 0; +contract DelegationMock is DelegationIntermediate { + mapping(address => bool) internal _isOperator; + mapping(address => mapping(IStrategy => uint256)) internal _weightOf; + function setOperatorShares(address operator, IStrategy strategy, uint256 actualWeight) external { + _weightOf[operator][strategy] = actualWeight; } - function minWithdrawalDelayBlocks() external view returns (uint256) { - return 50400; + function setIsOperator(address operator, bool isOperator) external { + _isOperator[operator] = isOperator; } - /** - * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, - * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). - */ - function strategyWithdrawalDelayBlocks(IStrategy /*strategy*/) external view returns (uint256) { - return 0; + function isOperator(address operator) external view override returns (bool) { + return _isOperator[operator]; } function getOperatorShares( address operator, - IStrategy[] memory strategies - ) external view returns (uint256[] memory) { + IStrategy[] calldata strategies + ) external view override returns (uint256[] memory) { uint256[] memory shares = new uint256[](strategies.length); - for (uint256 i = 0; i < strategies.length; ++i) { - shares[i] = operatorShares[operator][strategies[i]]; + for (uint256 i = 0; i < strategies.length; i++) { + shares[i] = _weightOf[operator][strategies[i]]; } return shares; } - - function getWithdrawalDelay(IStrategy[] calldata /*strategies*/) public view returns (uint256) { - return 0; - } - - function isDelegated(address staker) external view returns (bool) { - return (delegatedTo[staker] != address(0)); - } - - function isNotDelegated(address /*staker*/) external pure returns (bool) {} - - // function isOperator(address /*operator*/) external pure returns (bool) {} - - function stakerNonce(address /*staker*/) external pure returns (uint256) {} - - function delegationApproverSaltIsSpent(address /*delegationApprover*/, bytes32 /*salt*/) external pure returns (bool) {} - - function calculateCurrentStakerDelegationDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} - - function calculateStakerDelegationDigestHash(address /*staker*/, uint256 /*stakerNonce*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} - - function calculateDelegationApprovalDigestHash( - address /*staker*/, - address /*operator*/, - address /*_delegationApprover*/, - bytes32 /*approverSalt*/, - uint256 /*expiry*/ - ) external view returns (bytes32) {} - - function calculateStakerDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) - external pure returns (bytes32 stakerDigestHash) {} - - function calculateApproverDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) - external pure returns (bytes32 approverDigestHash) {} - - function calculateOperatorAVSRegistrationDigestHash(address /*operator*/, address /*avs*/, bytes32 /*salt*/, uint256 /*expiry*/) - external pure returns (bytes32 digestHash) {} - - function DOMAIN_TYPEHASH() external view returns (bytes32) {} - - function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32) {} - - function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} - - function domainSeparator() external view returns (bytes32) {} - - function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) {} - - function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) {} - - function operatorSaltIsSpent(address avs, bytes32 salt) external view returns (bool) {} - - function queueWithdrawals( - QueuedWithdrawalParams[] calldata queuedWithdrawalParams - ) external returns (bytes32[] memory) {} - - function completeQueuedWithdrawal( - Withdrawal calldata withdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex, - bool receiveAsTokens - ) external {} - - function completeQueuedWithdrawals( - Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, - bool[] calldata receiveAsTokens - ) external {} - - // onlyDelegationManager functions in StrategyManager - function addShares( - IStrategyManager strategyManager, - address staker, - IERC20 token, - IStrategy strategy, - uint256 shares - ) external { - strategyManager.addShares(staker, token, strategy, shares); - } - - function removeShares( - IStrategyManager strategyManager, - address staker, - IStrategy strategy, - uint256 shares - ) external { - strategyManager.removeShares(staker, strategy, shares); + function minWithdrawalDelayBlocks() external view override returns (uint32){ + return 10000; } - - function withdrawSharesAsTokens( - IStrategyManager strategyManager, - address recipient, - IStrategy strategy, - uint256 shares, - IERC20 token - ) external { - strategyManager.withdrawSharesAsTokens(recipient, strategy, shares, token); - } -} +} \ No newline at end of file diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 528270ae..102d076e 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -1,16 +1,24 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAServiceManagerBase.sol"; +import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { constructor( address _avsDirectory, address _stakeRegistry, address _rewardsCoordinator, - address _delegationManager + address _delegationManager, + address _allocationManager ) - ECDSAServiceManagerBase(_avsDirectory, _stakeRegistry, _rewardsCoordinator, _delegationManager) + ECDSAServiceManagerBase( + _avsDirectory, + _stakeRegistry, + _rewardsCoordinator, + _delegationManager, + _allocationManager + ) {} function initialize( @@ -19,4 +27,22 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { ) public virtual initializer { __ServiceManagerBase_init(initialOwner, rewardsInitiator); } + + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) external{} + + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external{} + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external{} + + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external{} + + function slashOperator(IAllocationManagerTypes.SlashingParams memory params) external override { + // Mock implementation - no actual slashing occurs + } } diff --git a/test/mocks/ECDSAStakeRegistryMock.sol b/test/mocks/ECDSAStakeRegistryMock.sol index 7ad6043e..e2b756be 100644 --- a/test/mocks/ECDSAStakeRegistryMock.sol +++ b/test/mocks/ECDSAStakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAStakeRegistry.sol"; diff --git a/test/mocks/EigenPodManagerMock.sol b/test/mocks/EigenPodManagerMock.sol new file mode 100644 index 00000000..700d1840 --- /dev/null +++ b/test/mocks/EigenPodManagerMock.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.9; + +import "forge-std/Test.sol"; +import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; + +contract EigenPodManagerMock is Test, Pausable, IEigenPodManager { + receive() external payable {} + fallback() external payable {} + + mapping(address => int256) public podShares; + + constructor(IPauserRegistry _pauserRegistry) Pausable(_pauserRegistry){ + _setPausedStatus(0); + } + + function podOwnerShares(address podOwner) external view returns (int256) { + return podShares[podOwner]; + } + + function setPodOwnerShares(address podOwner, int256 shares) external { + podShares[podOwner] = shares; + } + + function denebForkTimestamp() external pure returns (uint64) { + return type(uint64).max; + } + + function createPod() external returns (address) { + } + + function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable { + } + + function recordBeaconChainETHBalanceUpdate( + address podOwner, + int256 sharesDelta, + uint64 proportionPodBalanceDecrease + ) external { + } + + function ownerToPod(address podOwner) external view returns (IEigenPod) { + } + + function getPod(address podOwner) external view returns (IEigenPod) { + } + + function ethPOS() external view returns (IETHPOSDeposit) { + } + + function eigenPodBeacon() external view returns (IBeacon) { + } + + function strategyManager() external view returns (IStrategyManager) { + } + + function hasPod(address podOwner) external view returns (bool) { + } + + function numPods() external view returns (uint256) { + } + + function podOwnerDepositShares(address podOwner) external view returns (int256) { + } + + function beaconChainETHStrategy() external view returns (IStrategy) { + } + + function removeDepositShares(address staker, IStrategy strategy, uint256 depositSharesToRemove) external { + } + + function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 depositShares) { + } + + function withdrawSharesAsTokens(address staker, IStrategy strategy, IERC20 token, uint256 shares) external{} + + function addShares( + address staker, + IStrategy strategy, + IERC20 token, + uint256 shares + ) external returns (uint256, uint256) { + } + + function beaconChainSlashingFactor( + address staker + ) external view returns (uint64) { + } + + function recordBeaconChainETHBalanceUpdate( + address podOwner, + uint256 prevRestakedBalanceWei, + int256 balanceDeltaWei + ) external { + } + + function burnableETHShares() external view returns (uint256) { + } + + function increaseBurnableShares(IStrategy strategy, uint256 addedSharesToBurn) external { + } +} \ No newline at end of file diff --git a/test/mocks/PermissionControllerMock.sol b/test/mocks/PermissionControllerMock.sol new file mode 100644 index 00000000..9a9eca00 --- /dev/null +++ b/test/mocks/PermissionControllerMock.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; + +contract PermissionControllerIntermediate is IPermissionController { + function addPendingAdmin(address account, address admin) external virtual {} + + function removePendingAdmin( + address account, + address admin + ) external virtual {} + + function acceptAdmin(address account) external virtual {} + + function removeAdmin(address account, address admin) external virtual {} + + function setAppointee( + address account, + address appointee, + address target, + bytes4 selector + ) external virtual {} + + function removeAppointee( + address account, + address appointee, + address target, + bytes4 selector + ) external virtual {} + + function isAdmin( + address account, + address caller + ) external view virtual returns (bool) {} + + function isPendingAdmin( + address account, + address pendingAdmin + ) external view virtual returns (bool) {} + + function getAdmins( + address account + ) external view virtual returns (address[] memory) {} + + function getPendingAdmins( + address account + ) external view virtual returns (address[] memory) {} + + function canCall( + address account, + address caller, + address target, + bytes4 selector + ) external virtual returns (bool) {} + + function getAppointeePermissions( + address account, + address appointee + ) external virtual returns (address[] memory, bytes4[] memory) {} + + function getAppointees( + address account, + address target, + bytes4 selector + ) external virtual returns (address[] memory) {} +} + +contract PermissionControllerMock is PermissionControllerIntermediate { + mapping(address => mapping(address => mapping(address => mapping(bytes4 => bool)))) internal _canCall; + + function setCanCall( + address account, + address caller, + address target, + bytes4 selector + ) external { + _canCall[account][caller][target][selector] = true; + } + + function canCall( + address account, + address caller, + address target, + bytes4 selector + ) external override returns (bool) { + if (account == caller) return true; + return _canCall[account][caller][target][selector]; + } + +} diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index abee1a6a..165a649d 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IRegistryCoordinator.sol"; @@ -9,7 +9,7 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function blsApkRegistry() external view returns (IBLSApkRegistry) {} function ejectOperator( - address operator, + address operator, bytes calldata quorumNumbers ) external {} @@ -68,4 +68,14 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256) {} function owner() external view returns (address) {} -} + + function serviceManager() external view returns (IServiceManager){} + + function isM2Quorum(uint8 quorumNumber) external view returns (bool) { + return false; + } + + function isOperatorSetAVS() external view returns (bool) { + return false; + } +} \ No newline at end of file diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index 425c447d..9d992962 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -1,80 +1,171 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import "./AVSDirectoryMock.sol"; contract RewardsCoordinatorMock is IRewardsCoordinator { - /// @notice The address of the entity that can update the contract with new merkle roots - function rewardsUpdater() external view returns (address) {} + function initialize( + address initialOwner, + uint256 initialPausedStatus, + address _rewardsUpdater, + uint32 _activationDelay, + uint16 _defaultSplitBips + ) external override {} + + function createAVSRewardsSubmission( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} + + function createRewardsForAllSubmission( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} + + function createRewardsForAllEarners( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} + + function createOperatorDirectedAVSRewardsSubmission( + address avs, + OperatorDirectedRewardsSubmission[] + calldata operatorDirectedRewardsSubmissions + ) external override {} + + function processClaim( + RewardsMerkleClaim calldata claim, + address recipient + ) external override {} + + function processClaims( + RewardsMerkleClaim[] calldata claims, + address recipient + ) external override {} + + function submitRoot( + bytes32 root, + uint32 rewardsCalculationEndTimestamp + ) external override {} + + function disableRoot(uint32 rootIndex) external override {} + + function setClaimerFor(address claimer) external override {} + + function setClaimerFor(address earner, address claimer) external override {} + + function setActivationDelay(uint32 _activationDelay) external override {} + + function setDefaultOperatorSplit(uint16 split) external override {} + + function setOperatorAVSSplit( + address operator, + address avs, + uint16 split + ) external override {} + + function setOperatorPISplit( + address operator, + uint16 split + ) external override {} + + function setRewardsUpdater(address _rewardsUpdater) external override {} + + function setRewardsForAllSubmitter( + address _submitter, + bool _newValue + ) external override {} + + function activationDelay() external view override returns (uint32) {} + + function currRewardsCalculationEndTimestamp() + external + view + override + returns (uint32) + {} + + function claimerFor( + address earner + ) external view override returns (address) {} + + function cumulativeClaimed( + address claimer, + IERC20 token + ) external view override returns (uint256) {} + + function defaultOperatorSplitBips() external view override returns (uint16) {} + + function getOperatorAVSSplit( + address operator, + address avs + ) external view override returns (uint16) {} + + function getOperatorPISplit( + address operator + ) external view override returns (uint16) {} + + function calculateEarnerLeafHash( + EarnerTreeMerkleLeaf calldata leaf + ) external pure override returns (bytes32) {} + + function calculateTokenLeafHash( + TokenTreeMerkleLeaf calldata leaf + ) external pure override returns (bytes32) {} + + function checkClaim( + RewardsMerkleClaim calldata claim + ) external view override returns (bool) {} + + function getDistributionRootsLength() + external + view + override + returns (uint256) + {} + + function getDistributionRootAtIndex( + uint256 index + ) external view override returns (DistributionRoot memory) {} + + function getCurrentDistributionRoot() + external + view + override + returns (DistributionRoot memory) + {} + + function getCurrentClaimableDistributionRoot() + external + view + override + returns (DistributionRoot memory) + {} - function CALCULATION_INTERVAL_SECONDS() external view returns (uint32) {} + function getRootIndexFromHash( + bytes32 rootHash + ) external view override returns (uint32) {} - function MAX_REWARDS_DURATION() external view returns (uint32) {} + function rewardsUpdater() external view override returns (address) {} - function MAX_RETROACTIVE_LENGTH() external view returns (uint32) {} - - function MAX_FUTURE_LENGTH() external view returns (uint32) {} - - function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32) {} - - function activationDelay() external view returns (uint32) {} - - function claimerFor(address earner) external view returns (address) {} - - function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256) {} - - function globalOperatorCommissionBips() external view returns (uint16) {} - - function operatorCommissionBips(address operator, address avs) external view returns (uint16) {} - - function calculateEarnerLeafHash(EarnerTreeMerkleLeaf calldata leaf) external pure returns (bytes32) {} - - function calculateTokenLeafHash(TokenTreeMerkleLeaf calldata leaf) external pure returns (bytes32) {} - - function checkClaim(RewardsMerkleClaim calldata claim) external view returns (bool) {} - - function currRewardsCalculationEndTimestamp() external view returns (uint32) {} - - function getRootIndexFromHash(bytes32 rootHash) external view returns (uint32) {} - - function getDistributionRootsLength() external view returns (uint256) {} - - function getCurrentClaimableDistributionRoot() external view returns (DistributionRoot memory) {} - - function getDistributionRootAtIndex(uint256 index) external view returns (DistributionRoot memory) {} - - function getCurrentDistributionRoot() external view returns (DistributionRoot memory) {} - - /// EXTERNAL FUNCTIONS /// - - function disableRoot(uint32 rootIndex) external {} - - function createAVSRewardsSubmission(RewardsSubmission[] calldata rewardsSubmissions) external {} - - function createRewardsForAllSubmission(RewardsSubmission[] calldata rewardsSubmission) external {} - - function processClaim(RewardsMerkleClaim calldata claim, address recipient) external {} - - function submitRoot( - bytes32 root, - uint32 rewardsCalculationEndTimestamp - ) external {} - - function setRewardsUpdater(address _rewardsUpdater) external {} - - function setActivationDelay(uint32 _activationDelay) external {} - - function setGlobalOperatorCommission(uint16 _globalCommissionBips) external {} - - function setClaimerFor(address claimer) external {} - - /** - * @notice Sets the permissioned `payAllForRangeSubmitter` address which can submit payAllForRange - * @dev Only callable by the contract owner - * @param _submitter The address of the payAllForRangeSubmitter - * @param _newValue The new value for isPayAllForRangeSubmitter - */ - function setRewardsForAllSubmitter(address _submitter, bool _newValue) external {} + function CALCULATION_INTERVAL_SECONDS() + external + view + override + returns (uint32) + {} + + function MAX_REWARDS_DURATION() external view override returns (uint32) {} + + function MAX_RETROACTIVE_LENGTH() external view override returns (uint32) {} + + function MAX_FUTURE_LENGTH() external view override returns (uint32) {} + + function GENESIS_REWARDS_TIMESTAMP() + external + view + override + returns (uint32) + {} } \ No newline at end of file diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 8af99426..a60e63c8 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/ServiceManagerBase.sol"; @@ -8,15 +8,23 @@ contract ServiceManagerMock is ServiceManagerBase { IAVSDirectory _avsDirectory, IRewardsCoordinator _rewardsCoordinator, IRegistryCoordinator _registryCoordinator, - IStakeRegistry _stakeRegistry + IStakeRegistry _stakeRegistry, + IAllocationManager _allocationManager ) - ServiceManagerBase(_avsDirectory, _rewardsCoordinator, _registryCoordinator, _stakeRegistry) + ServiceManagerBase( + _avsDirectory, + _rewardsCoordinator, + _registryCoordinator, + _stakeRegistry, + _allocationManager + ) {} function initialize( address initialOwner, - address rewardsInitiator + address rewardsInitiator, + address slasher ) public virtual initializer { - __ServiceManagerBase_init(initialOwner, rewardsInitiator); + __ServiceManagerBase_init(initialOwner, rewardsInitiator, slasher); } } diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index f86b938f..58f46256 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IStakeRegistry.sol"; import "../../src/interfaces/IRegistryCoordinator.sol"; @@ -18,6 +18,19 @@ contract StakeRegistryMock is IStakeRegistry { function registryCoordinator() external view returns (address) {} + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory _strategyParams + ) external {} + + function initializeSlashableStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + uint32 lookAheadPeriod, + StrategyParams[] memory _strategyParams + ) external {} + /** * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. * @param operator The address of the operator to register. @@ -32,8 +45,8 @@ contract StakeRegistryMock is IStakeRegistry { * 4) the operator is not already registered */ function registerOperator( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes memory quorumNumbers ) external returns (uint96[] memory, uint96[] memory) {} @@ -149,7 +162,7 @@ contract StakeRegistryMock is IStakeRegistry { /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the - * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry + * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry * corresponds to the operator's stake at `blockNumber`. Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param operatorId The id of the operator of interest. @@ -164,8 +177,8 @@ contract StakeRegistryMock is IStakeRegistry { returns (uint96) {} /** - * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the - * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. + * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the + * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. * Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. @@ -201,8 +214,8 @@ contract StakeRegistryMock is IStakeRegistry { * added to the */ function updateOperatorStake( - address /*operator*/, - bytes32 /*operatorId*/, + address /*operator*/, + bytes32 /*operatorId*/, bytes calldata /*quorumNumbers*/ ) external returns (uint192) { return updateOperatorStakeReturnBitmap; diff --git a/test/unit/AVSRegistrar.t.sol b/test/unit/AVSRegistrar.t.sol new file mode 100644 index 00000000..deea28a2 --- /dev/null +++ b/test/unit/AVSRegistrar.t.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {AVSRegistrarMock} from "../mocks/AVSRegistrarMock.sol"; +import {console2 as console} from "forge-std/Test.sol"; + +contract AVSRegistrarTest is MockAVSDeployer { + using BN254 for BN254.G1Point; + + AVSRegistrarMock public avsRegistrarMock; + address internal operator = address(420); + + function setUp() public virtual { + _deployMockEigenLayerAndAVS(); + avsRegistrarMock = new AVSRegistrarMock(); + } + + function testSetAVSRegistrar() public { + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar(address(serviceManager), IAVSRegistrar(address(avsRegistrarMock))); + assertEq(address(allocationManager.getAVSRegistrar(address(serviceManager))), address(avsRegistrarMock)); + } + + function testRegisterOperator() public { + // Set up AVS registrar + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar(address(serviceManager), IAVSRegistrar(address(avsRegistrarMock))); + + // Create operator set + uint32 operatorSetId = 1; + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: operatorSetId, + strategies: new IStrategy[](0) + }); + + // Create operator set + vm.prank(address(serviceManager)); + allocationManager.createOperatorSets(address(serviceManager), createSetParams); + + // Set up registration params + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = operatorSetId; + bytes memory emptyBytes; + + delegationMock.setIsOperator(operator, true); + + // Register operator + vm.prank(operator); + allocationManager.registerForOperatorSets( + address(operator), + IAllocationManagerTypes.RegisterParams(address(serviceManager), operatorSetIds, emptyBytes) + ); + } + + function testRegisterOperator_RevertsIfNotOperator() public { + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar(address(serviceManager), IAVSRegistrar(address(avsRegistrarMock))); + + // Create operator set + uint32 operatorSetId = 1; + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: operatorSetId, + strategies: new IStrategy[](0) + }); + + // Create operator set + vm.prank(address(serviceManager)); + allocationManager.createOperatorSets(address(serviceManager), createSetParams); + + // Set up registration params + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = operatorSetId; + bytes memory emptyBytes; + + delegationMock.setIsOperator(operator, false); + + // Register operator + vm.prank(operator); + + vm.expectRevert(); + allocationManager.registerForOperatorSets( + address(operator), + IAllocationManagerTypes.RegisterParams(address(serviceManager), operatorSetIds, emptyBytes) + ); + } + function testAllocationManagerDeployed() public { + assertTrue(address(allocationManager) != address(0), "AllocationManager not deployed"); + assertTrue(address(allocationManagerImplementation) != address(0), "AllocationManager implementation not deployed"); + } +} diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 5f800444..fa644cae 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "../harnesses/BLSApkRegistryHarness.sol"; @@ -8,6 +8,7 @@ import "../harnesses/BitmapUtilsWrapper.sol"; import "../utils/BLSMockAVSDeployer.sol"; import {IBLSApkRegistryEvents} from "../events/IBLSApkRegistryEvents.sol"; +import {IBLSApkRegistryErrors} from "../../src/interfaces/IBLSApkRegistry.sol"; contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { using BitmapUtils for uint192; @@ -309,9 +310,7 @@ contract BLSApkRegistryUnitTests_configAndGetters is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(address(nonCoordinatorAddress)); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.initializeQuorum(defaultQuorumNumber); } } @@ -334,9 +333,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is .pubkeyRegistrationMessageHash(defaultOperator); cheats.prank(address(nonCoordinatorAddress)); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.registerBLSPublicKey( defaultOperator, pubkeyRegistrationParams, @@ -353,9 +350,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is .pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); + cheats.expectRevert(IBLSApkRegistryErrors.ZeroPubKey.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -379,9 +374,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is messageHash ); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorAlreadyRegistered.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -412,9 +405,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is messageHash ); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BLSPubkeyAlreadyRegistered.selector); blsApkRegistry.registerBLSPublicKey( operator2, pubkeyRegistrationParams, @@ -442,9 +433,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is pubkeyRegistrationParams.pubkeyRegistrationSignature = invalidSignature; cheats.startPrank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" - ); + cheats.expectRevert(IBLSApkRegistryErrors.InvalidBLSSignatureOrPrivateKey.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -468,9 +457,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is BN254.G1Point memory messageHash = registryCoordinator .pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" - ); + cheats.expectRevert(IBLSApkRegistryErrors.InvalidBLSSignatureOrPrivateKey.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -544,9 +531,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(nonCoordinatorAddress); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.registerOperator(nonCoordinatorAddress, new bytes(0)); } @@ -554,9 +539,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { address operator ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorNotRegistered.selector); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -577,9 +560,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { _registerDefaultBLSPubkey(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" - ); + cheats.expectRevert(IBLSApkRegistryErrors.QuorumDoesNotExist.selector); blsApkRegistry.registerOperator(operator, quorumNumbers); } @@ -672,9 +653,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(nonCoordinatorAddress); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0)); } @@ -682,9 +661,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { address operator ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorNotRegistered.selector); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -708,9 +685,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { _registerOperator(operator, validQuorumNumbers); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" - ); + cheats.expectRevert(IBLSApkRegistryErrors.QuorumDoesNotExist.selector); blsApkRegistry.deregisterOperator(operator, invalidQuorumNumbers); } @@ -1080,9 +1055,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { } if (wrongBlockNumber < startingBlockNumber + indexToCheck * 100) { emit log_named_uint("index too recent: ", indexToCheck); - cheats.expectRevert( - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberTooRecent.selector); blsApkRegistry.getApkHashAtBlockNumberAndIndex( defaultQuorumNumber, wrongBlockNumber, @@ -1093,9 +1066,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { wrongBlockNumber >= startingBlockNumber + (indexToCheck + 1) * 100 ) { emit log_named_uint("index not latest: ", indexToCheck); - cheats.expectRevert( - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberNotLatest.selector); blsApkRegistry.getApkHashAtBlockNumberAndIndex( defaultQuorumNumber, wrongBlockNumber, diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 29369b3f..d1d0ac7f 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -1,15 +1,19 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSSignatureChecker.sol"; import "../utils/BLSMockAVSDeployer.sol"; +import {IBLSSignatureCheckerErrors} from "../../src/interfaces/IBLSSignatureChecker.sol"; +import {IBLSApkRegistryErrors} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +import {IStakeRegistryErrors} from "../../src/interfaces/IStakeRegistry.sol"; contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { using BN254 for BN254.G1Point; BLSSignatureChecker blsSignatureChecker; - event StaleStakesForbiddenUpdate(bool value); + event StaleStakesForbiddenUpdate(bool value); function setUp() virtual public { _setUpBLSMockAVSDeployer(); @@ -18,7 +22,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { } function test_setStaleStakesForbidden_revert_notRegCoordOwner() public { - cheats.expectRevert("BLSSignatureChecker.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + cheats.expectRevert(IBLSSignatureCheckerErrors.OnlyRegistryCoordinatorOwner.selector); blsSignatureChecker.setStaleStakesForbidden(true); } @@ -38,12 +42,12 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and // the signature is only checked for stakes on that quorum - function testFuzz_checkSignatures_SingleQuorum(uint256 pseudoRandomNumber) public { + function testFuzz_checkSignatures_SingleQuorum(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); bytes32[] memory pubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); @@ -57,9 +61,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -78,7 +82,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); bytes32[] memory pubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); @@ -92,9 +96,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -109,13 +113,13 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums // and the signature is only checked for stakes on those quorums - function test_checkSignatures_100Quorums(uint256 pseudoRandomNumber) public { + function test_checkSignatures_100Quorums(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); // 100 set bits uint256 quorumBitmap = (1 << 100) - 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); @@ -132,16 +136,16 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); for (uint256 i = 0; i < quorumStakeTotals.signedStakeForQuorum.length; ++i) { - assertTrue(quorumStakeTotals.signedStakeForQuorum[i] > 0, "signedStakeForQuorum should be nonzero"); + assertTrue(quorumStakeTotals.signedStakeForQuorum[i] > 0, "signedStakeForQuorum should be nonzero"); } assertEq(expectedSignatoryRecordHash, signatoryRecordHash, "signatoryRecordHash does not match expectation"); } @@ -150,7 +154,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 numNonSigners = 0; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(1, numNonSigners, quorumBitmap); IBLSSignatureChecker.NonSignerStakesAndSignature memory incorrectLengthInputs = IBLSSignatureChecker.NonSignerStakesAndSignature({ @@ -166,11 +170,11 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // make one part of the input incorrect length incorrectLengthInputs.quorumApks = new BN254.G1Point[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -178,11 +182,11 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.quorumApks = nonSignerStakesAndSignature.quorumApks; // make one part of the input incorrect length incorrectLengthInputs.quorumApkIndices = new uint32[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -190,11 +194,11 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.quorumApkIndices = nonSignerStakesAndSignature.quorumApkIndices; // make one part of the input incorrect length incorrectLengthInputs.totalStakeIndices = new uint32[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -202,11 +206,11 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.totalStakeIndices = nonSignerStakesAndSignature.totalStakeIndices; // make one part of the input incorrect length incorrectLengthInputs.nonSignerStakeIndices = new uint32[][](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -214,11 +218,11 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.nonSignerStakeIndices = nonSignerStakesAndSignature.nonSignerStakeIndices; // make one part of the input incorrect length incorrectLengthInputs.nonSignerQuorumBitmapIndices = new uint32[](nonSignerStakesAndSignature.nonSignerPubkeys.length + 1); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input nonsigner length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputNonSignerLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -226,9 +230,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.nonSignerQuorumBitmapIndices = nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices; // sanity check for call passing with the correct values blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); } @@ -238,16 +242,16 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (/*uint32 referenceBlockNumber*/, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (/*uint32 referenceBlockNumber*/, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // Create an invalid reference block: any block number >= the current block uint32 invalidReferenceBlock = uint32(block.number + (pseudoRandomNumber % 20)); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: invalid reference block"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidReferenceBlocknumber.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - invalidReferenceBlock, + invalidReferenceBlock, nonSignerStakesAndSignature ); } @@ -258,16 +262,16 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); - + // swap out a pubkey to make sure there is a duplicate nonSignerStakesAndSignature.nonSignerPubkeys[1] = nonSignerStakesAndSignature.nonSignerPubkeys[0]; - cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); + cheats.expectRevert(IBLSSignatureCheckerErrors.NonSignerPubkeysNotSorted.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -278,17 +282,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); - + // swap two pubkeys to ensure ordering is wrong (nonSignerStakesAndSignature.nonSignerPubkeys[0], nonSignerStakesAndSignature.nonSignerPubkeys[1]) = (nonSignerStakesAndSignature.nonSignerPubkeys[1], nonSignerStakesAndSignature.nonSignerPubkeys[0]); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); + cheats.expectRevert(IBLSSignatureCheckerErrors.NonSignerPubkeysNotSorted.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -299,12 +303,12 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); // make sure the `staleStakesForbidden` flag is set to 'true' testFuzz_setStaleStakesForbidden(true); - + uint256 stalestUpdateBlock = type(uint256).max; for (uint256 i = 0; i < quorumNumbers.length; ++i) { uint256 quorumUpdateBlockNumber = registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])); @@ -320,7 +324,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { cheats.roll(referenceBlockNumber + 1); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature @@ -330,11 +334,11 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { referenceBlockNumber += 1; // roll forward to reference + 1 to ensure the referenceBlockNumber is still valid cheats.roll(referenceBlockNumber + 1); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window"); + cheats.expectRevert(IBLSSignatureCheckerErrors.StaleStakesForbidden.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -344,20 +348,20 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // record a quorumBitmap update via a harnessed function registryCoordinator._updateOperatorBitmapExternal(nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(), uint192(quorumBitmap | 2)); // set the nonSignerQuorumBitmapIndices to a different value nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[0] = 1; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -367,17 +371,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the totalStakeIndices to a different value nonSignerStakesAndSignature.totalStakeIndices[0] = 0; - cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber"); + cheats.expectRevert(IStakeRegistryErrors.InvalidBlockNumber.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -387,26 +391,26 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); bytes32 nonSignerOperatorId = nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(); - + // record a stake update stakeRegistry.recordOperatorStakeUpdate( - nonSignerOperatorId, - uint8(quorumNumbers[0]), + nonSignerOperatorId, + uint8(quorumNumbers[0]), 1234 ); - + // set the nonSignerStakeIndices to a different value nonSignerStakesAndSignature.nonSignerStakeIndices[0][0] = 1; - cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber"); + cheats.expectRevert(IStakeRegistryErrors.InvalidBlockNumber.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); @@ -417,17 +421,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); // set the quorumApkIndices to a different value nonSignerStakesAndSignature.quorumApkIndices[0] = 0; - cheats.expectRevert("BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update"); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberNotLatest.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -437,17 +441,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the quorumApk to a different value nonSignerStakesAndSignature.quorumApks[0] = nonSignerStakesAndSignature.quorumApks[0].negate(); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidQuorumApkHash.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -457,17 +461,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the sigma to a different value nonSignerStakesAndSignature.sigma = nonSignerStakesAndSignature.sigma.negate(); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: signature is invalid"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidBLSSignature.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -477,18 +481,18 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the sigma to a different value nonSignerStakesAndSignature.sigma.X++; // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call cheats.expectRevert(); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -498,18 +502,18 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // Create an empty quorumNumbers array bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(0); // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call - cheats.expectRevert("BLSSignatureChecker.checkSignatures: empty quorum input"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputEmptyQuorumNumbers.selector); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } diff --git a/test/unit/BitmapUtils.t.sol b/test/unit/BitmapUtils.t.sol index fd51298d..a998dbe3 100644 --- a/test/unit/BitmapUtils.t.sol +++ b/test/unit/BitmapUtils.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../harnesses/BitmapUtilsWrapper.sol"; // import "../../contracts/libraries/BitmapUtils.sol"; @@ -24,7 +24,7 @@ contract BitmapUtilsUnitTests_bitwiseOperations is BitmapUtilsUnitTests { uint16 numOnes = 0; for (uint256 i = 0; i < 256; ++i) { if ((input >> i) & 1 == 1) { - ++numOnes; + ++numOnes; } } assertEq(libraryOutput, numOnes, "inconsistency in countNumOnes function"); @@ -185,7 +185,7 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { /// when calling orderedBytesArrayToBitmap function testFuzz_OrderedBytesArrayToBitmap_Revert_WhenNotOrdered(bytes memory originalBytesArray) public { cheats.assume(!bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray)); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered"); + cheats.expectRevert(BitmapUtils.BytesArrayNotOrdered.selector); bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); } diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index 3b533d47..bcd17052 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -1,186 +1,192 @@ -// 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); - } -} +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.27; + +// 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 MockAllocationManager {} + +// contract MockRewardsCoordinator { +// function createAVSRewardsSubmission( +// address avs, +// IRewardsCoordinator.RewardsSubmission[] calldata +// ) external pure {} +// } + +// contract ECDSAServiceManagerSetup is Test { +// MockDelegationManager public mockDelegationManager; +// MockAVSDirectory public mockAVSDirectory; +// MockAllocationManager public mockAllocationManager; +// 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(); +// mockAllocationManager = new MockAllocationManager(); +// mockStakeRegistry = new ECDSAStakeRegistryMock( +// IDelegationManager(address(mockDelegationManager)) +// ); +// mockRewardsCoordinator = new MockRewardsCoordinator(); + +// serviceManager = new ECDSAServiceManagerMock( +// address(mockAVSDirectory), +// address(mockStakeRegistry), +// address(mockRewardsCoordinator), +// address(mockDelegationManager), +// address(mockAllocationManager) +// ); + +// 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)); + +// uint96[] memory shares = new uint96[](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); +// } +// } diff --git a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol index bc6337c5..0008da7f 100644 --- a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol index dffb9174..8205034d 100644 --- a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index d374144d..e1d7de77 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index a70b2689..a95366f7 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {EjectionManager} from "../../src/EjectionManager.sol"; -import {IEjectionManager} from "../../src/interfaces/IEjectionManager.sol"; +import {IEjectionManager, IEjectionManagerErrors} from "../../src/interfaces/IEjectionManager.sol"; import "../utils/MockAVSDeployer.sol"; @@ -366,7 +366,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { function test_Revert_NotPermissioned() public { bytes32[][] memory operatorIds; - cheats.expectRevert("Ejector: Only owner or ejector can eject"); + cheats.expectRevert(IEjectionManagerErrors.OnlyOwnerOrEjector.selector); ejectionManager.ejectOperators(operatorIds); EjectionManager.QuorumEjectionParams memory _quorumEjectionParams; diff --git a/test/unit/End2End.t.sol b/test/unit/End2End.t.sol new file mode 100644 index 00000000..9a39ada1 --- /dev/null +++ b/test/unit/End2End.t.sol @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Test, console2 as console} from "forge-std/Test.sol"; +import {IERC20} from "forge-std/interfaces/IERC20.sol"; +import {OperatorLib} from "../../script/utils/OperatorLib.sol"; +import {CoreDeploymentLib} from "../../script/utils/CoreDeploymentLib.sol"; +import {UpgradeableProxyLib} from "../../script/utils/UpgradeableProxyLib.sol"; +import {MiddlewareDeploymentLib} from "../../script/utils/MiddlewareDeploymentLib.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {IDelegationManager} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAllocationManagerTypes} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {IStakeRegistry, StakeType} from "../../src/interfaces/IStakeRegistry.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; + +contract End2EndForkTest is Test { + using OperatorLib for *; + + address internal proxyAdmin; + + function _createOperators(uint256 numOperators, uint256 startIndex) internal returns (OperatorLib.Operator[] memory) { + OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](numOperators); + for (uint256 i = 0; i < numOperators; i++) { + operators[i] = OperatorLib.createOperator(string(abi.encodePacked("operator-", i + startIndex))); + } + return operators; + } + + function _registerOperatorsAsEigenLayerOperators( + OperatorLib.Operator[] memory operators, + address delegationManager + ) internal { + for (uint256 i = 0; i < operators.length; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerAsOperator(operators[i], delegationManager); + vm.stopPrank(); + } + } + + function testCreateOperator() public { + OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); + + assertTrue(operator.key.addr != address(0), "VM wallet address should be non-zero"); + + assertTrue(operator.signingKey.privateKey != 0, "BLS private key should be non-zero"); + + assertTrue(operator.signingKey.publicKeyG1.X != 0 || operator.signingKey.publicKeyG1.X != 0, "BLS public key G1 X should be non-zero"); + assertTrue(operator.signingKey.publicKeyG1.Y != 0 || operator.signingKey.publicKeyG1.Y!= 0, "BLS public key G1 Y should be non-zero"); + + assertTrue(operator.signingKey.publicKeyG2.X[0] != 0 || operator.signingKey.publicKeyG2.X[1] != 0, "BLS public key G2 X should be non-zero"); + assertTrue(operator.signingKey.publicKeyG2.Y[0] != 0 || operator.signingKey.publicKeyG2.Y[1] != 0, "BLS public key G2 Y should be non-zero"); + } + + function testSignAndVerifyMessage() public { + OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); + + bytes32 messageHash = keccak256(abi.encodePacked("Test message")); + BN254.G1Point memory signature = OperatorLib.signMessageWithOperator(operator, messageHash); + BN254.G1Point memory messagePoint = BN254.hashToG1(messageHash); + + bool isValid = BN254.pairing( + BN254.negate(signature), + BN254.generatorG2(), + messagePoint, + operator.signingKey.publicKeyG2 + ); + assertTrue(isValid, "Signature should be valid"); + } + + function testEndToEndSetup() public { + (OperatorLib.Operator[] memory operators, CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig) = _setupInitialState(); + + _setupOperatorsAndTokens(operators, coreDeployment, middlewareDeployment); + + _setupFirstQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); + + _setupSecondQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); + + _executeSlashing(operators, middlewareConfig, middlewareDeployment); + } + + function _setupInitialState() internal returns ( + OperatorLib.Operator[] memory operators, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig + ) { + // Fork Holesky testnet + string memory rpcUrl = vm.envString("HOLESKY_RPC_URL"); + vm.createSelectFork(rpcUrl); + + // Create 5 operators using helper function + operators = _createOperators(5, 100); + + // Read core deployment data from json + coreDeployment = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + + // Setup middleware deployment data + proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + middlewareConfig.admin = address(this); + middlewareConfig.numQuorums = 1; + middlewareConfig.operatorParams = new uint256[](3); + middlewareConfig.operatorParams[0] = 10; + middlewareConfig.operatorParams[1] = 100; + middlewareConfig.operatorParams[2] = 100; + + middlewareDeployment = MiddlewareDeploymentLib.deployContracts(proxyAdmin, coreDeployment, middlewareConfig); + MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, middlewareConfig, coreDeployment); + } + + function _setupOperatorsAndTokens( + OperatorLib.Operator[] memory operators, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + // Verify and register operators + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); + assertFalse(isRegistered, "Operator should not be registered"); + } + + _registerOperatorsAsEigenLayerOperators(operators, coreDeployment.delegationManager); + + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); + assertTrue(isRegistered, "Operator should be registered"); + } + + // Setup tokens and verify balances + uint256 mintAmount = 1000 * 1e18; + for (uint256 i = 0; i < 5; i++) { + OperatorLib.mintMockTokens(operators[i], middlewareDeployment.token, mintAmount); + uint256 balance = IERC20(middlewareDeployment.token).balanceOf(operators[i].key.addr); + assertEq(balance, mintAmount, "Operator should have correct token balance"); + } + + // Handle deposits + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + uint256 shares = OperatorLib.depositTokenIntoStrategy( + operators[i], + coreDeployment.strategyManager, + middlewareDeployment.strategy, + middlewareDeployment.token, + mintAmount + ); + assertTrue(shares > 0, "Should have received shares for deposit"); + vm.stopPrank(); + + shares = IStrategy(middlewareDeployment.strategy).shares(operators[i].key.addr); + assertEq(shares, mintAmount, "Operator shares should equal deposit amount"); + } + } + + function _setupFirstQuorumAndOperatorSet( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + vm.startPrank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).enableOperatorSets(); + + // Create first quorum + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 100, + kickBIPsOfTotalStake: 100 + }); + + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(middlewareDeployment.strategy), + multiplier: 1 ether + }); + + RegistryCoordinator(middlewareDeployment.registryCoordinator).createTotalDelegatedStakeQuorum( + operatorSetParams, + 100, + strategyParams + ); + vm.stopPrank(); + + // Register operators + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 1; + + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds + ); + vm.stopPrank(); + } + + vm.roll(block.number + 10); + + // Update operators for quorum + address[][] memory registeredOperators = _getAndSortOperators(operators); + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(1)); + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators, + quorumNumbers + ); + } + + function _setupSecondQuorumAndOperatorSet( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + // Create second quorum + vm.startPrank(middlewareConfig.admin); + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(middlewareDeployment.strategy), + multiplier: 1 ether + }); + + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + + RegistryCoordinator(middlewareDeployment.registryCoordinator).createSlashableStakeQuorum( + operatorSetParams, + 100, + strategyParams, + 1 days + ); + vm.stopPrank(); + + _setupOperatorAllocations(operators, coreDeployment, middlewareDeployment); + + // Register and update operators for second quorum + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 2; + + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds + ); + vm.stopPrank(); + } + + vm.roll(block.number + 10); + + address[][] memory registeredOperators = _getAndSortOperators(operators); + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(2)); + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators, + quorumNumbers + ); + } + + function _setupOperatorAllocations( + OperatorLib.Operator[] memory operators, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + uint32 minDelay = 1; + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.setAllocationDelay( + operators[i], + address(coreDeployment.allocationManager), + minDelay + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + + IStrategy[] memory allocStrategies = new IStrategy[](1); + allocStrategies[0] = IStrategy(middlewareDeployment.strategy); + + uint64[] memory magnitudes = new uint64[](1); + magnitudes[0] = uint64(1 ether); + + OperatorSet memory operatorSet = OperatorSet({ + avs: address(middlewareDeployment.serviceManager), + id: 2 + }); + + IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + allocParams[0] = IAllocationManagerTypes.AllocateParams({ + operatorSet: operatorSet, + strategies: allocStrategies, + newMagnitudes: magnitudes + }); + + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.modifyOperatorAllocations( + operators[i], + address(coreDeployment.allocationManager), + allocParams + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + } + + function _executeSlashing( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + address newSlasher = makeAddr("newSlasher"); + vm.startPrank(middlewareConfig.admin); + ServiceManagerMock(middlewareDeployment.serviceManager).proposeNewSlasher(newSlasher); + vm.warp(block.timestamp + 8 days); + ServiceManagerMock(middlewareDeployment.serviceManager).acceptProposedSlasher(); + vm.stopPrank(); + + IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes.SlashingParams({ + operator: operators[0].key.addr, + operatorSetId: 2, + strategies: new IStrategy[](1), + wadsToSlash: new uint256[](1), + description: "Test slashing" + }); + + slashingParams.strategies[0] = IStrategy(middlewareDeployment.strategy); + slashingParams.wadsToSlash[0] = 0.5e18; + + vm.prank(newSlasher); + ServiceManagerMock(middlewareDeployment.serviceManager).slashOperator(slashingParams); + } + + function _getAndSortOperators(OperatorLib.Operator[] memory operators) internal pure returns (address[][] memory) { + address[][] memory registeredOperators = new address[][](1); + registeredOperators[0] = new address[](5); + for (uint256 i = 0; i < 5; i++) { + registeredOperators[0][i] = operators[i].key.addr; + } + + // Sort operator addresses + for (uint256 i = 0; i < registeredOperators[0].length - 1; i++) { + for (uint256 j = 0; j < registeredOperators[0].length - i - 1; j++) { + if (registeredOperators[0][j] > registeredOperators[0][j + 1]) { + address temp = registeredOperators[0][j]; + registeredOperators[0][j] = registeredOperators[0][j + 1]; + registeredOperators[0][j + 1] = temp; + } + } + } + + return registeredOperators; + } + + function testEndToEndSetup_M2Migration() public { + (OperatorLib.Operator[] memory operators, CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig) = _setupInitialState(); + + _setupOperatorsAndTokens(operators, coreDeployment, middlewareDeployment); + + _setupFirstQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); + + _setupSecondQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); + + _executeSlashing(operators, middlewareConfig, middlewareDeployment); + } + + function _setupFirstQuorumAndOperatorSet_M2( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + // Register operators to AVS through AllocationManager + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 1; // First operator set + + // Register each operator to the AVS through M2 + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(1)); // Quorum 1 + OperatorLib.registerOperatorToAVS_M2( + operators[i], + coreDeployment.avsDirectory, + middlewareDeployment.serviceManager, + middlewareDeployment.registryCoordinator, + quorumNumbers, + "test-socket" + ); + vm.stopPrank(); + } + + // Fast forward 10 blocks + vm.roll(block.number + 10); + + // Get all registered operators and sort them + address[][] memory registeredOperators = _getAndSortOperators(operators); + + // Update operators for quorum 1 + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(1)); // Quorum 1 + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators, + quorumNumbers + ); + + // Enable operator sets + // Migrate AVS to operator sets + vm.startPrank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).enableOperatorSets(); + vm.stopPrank(); + } + + function _setupSecondQuorumAndOperatorSet_M2( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + // Create a second operator set for slashable stake + IStakeRegistry.StrategyParams[] memory strategyParams2 = new IStakeRegistry.StrategyParams[](1); + strategyParams2[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(middlewareDeployment.strategy), + multiplier: 1 ether + }); + + // Configure operator set params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams2 = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + + // Create quorum with slashable stake type + vm.startPrank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).createSlashableStakeQuorum( + operatorSetParams2, + 100, // minimumStake + strategyParams2, + 1 days // lookAheadPeriod + ); + vm.stopPrank(); + + // Set allocation delay to 1 block for each operator + uint32 minDelay = 1; + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.setAllocationDelay( + operators[i], + address(coreDeployment.allocationManager), + minDelay + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + + // Set up allocation parameters for each operator + IStrategy[] memory allocStrategies = new IStrategy[](1); + allocStrategies[0] = IStrategy(middlewareDeployment.strategy); + + uint64[] memory magnitudes = new uint64[](1); + magnitudes[0] = uint64(1 ether); // Allocate full magnitude to meet minimum stake + + OperatorSet memory operatorSet = OperatorSet({ + avs: address(middlewareDeployment.serviceManager), + id: 2 // Second operator set + }); + + IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + allocParams[0] = IAllocationManagerTypes.AllocateParams({ + operatorSet: operatorSet, + strategies: allocStrategies, + newMagnitudes: magnitudes + }); + + // Allocate stake for each operator using helper function + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.modifyOperatorAllocations( + operators[i], + address(coreDeployment.allocationManager), + allocParams + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + + // Register operators to second operator set + uint32[] memory operatorSetIds2 = new uint32[](1); + operatorSetIds2[0] = 2; // Second operator set + + // Register each operator to the second set + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds2 + ); + vm.stopPrank(); + } + + vm.roll(block.number + 10); + + // Get all registered operators for second set and sort them + address[][] memory registeredOperators2 = _getAndSortOperators(operators); + + // Update operators for quorum 2 + bytes memory quorumNumbers2 = new bytes(1); + quorumNumbers2[0] = bytes1(uint8(2)); // Quorum 2 + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators2, + quorumNumbers2 + ); + } +} diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 1ed44d7d..79d74e91 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IIndexRegistry.sol"; import "../../src/IndexRegistry.sol"; @@ -63,7 +63,7 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); initializedQuorumBytes = initializedQuorumBitmap.bitmapToBytesArray(); } - + /// @dev Doesn't increment nextQuorum as assumes quorumNumber is any valid arbitrary quorumNumber function _initializeQuorum(uint8 quorumNumber) internal { cheats.prank(address(registryCoordinator)); @@ -141,7 +141,7 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { function _randUint(bytes32 rand, uint min, uint max) internal pure returns (uint) { // hashing makes for more uniform randomness rand = keccak256(abi.encodePacked(rand)); - + uint range = max - min + 1; // calculate the number of bits needed for the range @@ -258,7 +258,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { currBlockNumber += 10; cheats.roll(currBlockNumber); - + // initialize a new quorum after startBlockNumber uint8 quorumNumber = nextQuorum; _initializeQuorum(); @@ -268,7 +268,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { for (uint256 i = 0; i < numOperators; i++) { uint256 rand = _randUint({ rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, + min: 0, max: 1 }); @@ -293,6 +293,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { // should revert with startBlocknumber cheats.expectRevert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); + indexRegistry.getOperatorListAtBlockNumber( quorumNumber, startBlockNumber @@ -312,7 +313,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { for (uint256 i = 0; i < numOperators; i++) { uint256 rand = _randUint({ rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, + min: 0, max: 1 }); @@ -359,7 +360,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { for (uint256 i = 0; i < numOperators; i++) { uint256 rand = _randUint({ rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, + min: 0, max: 1 }); @@ -418,7 +419,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert(IIndexRegistryErrors.OnlyRegistryCoordinator.selector); indexRegistry.registerOperator(bytes32(0), quorumNumbers); } @@ -439,7 +440,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Register for invalid quorums, should revert bytes memory invalidQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(invalidBitmap); cheats.prank(address(registryCoordinator)); - cheats.expectRevert("IndexRegistry.registerOperator: quorum does not exist"); + cheats.expectRevert(IIndexRegistryErrors.QuorumDoesNotExist.selector); indexRegistry.registerOperator(operatorId1, invalidQuorumNumbers); } @@ -465,7 +466,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory updates _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); // Check _indexHistory updates @@ -509,7 +510,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory and _indexHistory updates for quorum 1 _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); _assertOperatorUpdate({ @@ -523,7 +524,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory and _indexHistory updates for quorum 2 _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber + 1, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); _assertOperatorUpdate({ @@ -573,7 +574,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 2, + expectedNumOperators: 2, expectedFromBlockNumber: block.number }); } @@ -619,7 +620,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: uint8(quorumNumbers[i]), - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); } @@ -670,7 +671,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: uint8(quorumNumbers[j]), - expectedNumOperators: i + 1, + expectedNumOperators: i + 1, expectedFromBlockNumber: block.number }); } @@ -699,7 +700,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert(IIndexRegistryErrors.OnlyRegistryCoordinator.selector); indexRegistry.deregisterOperator(bytes32(0), quorumNumbers); } @@ -724,7 +725,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Deregister for invalid quorums, should revert cheats.prank(address(registryCoordinator)); - cheats.expectRevert("IndexRegistry.registerOperator: quorum does not exist"); + cheats.expectRevert(IIndexRegistryErrors.QuorumDoesNotExist.selector); indexRegistry.deregisterOperator(operatorId1, invalidQuorumNumbers); } @@ -761,7 +762,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 0, + expectedNumOperators: 0, expectedFromBlockNumber: block.number }); } @@ -800,7 +801,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 0, + expectedNumOperators: 0, expectedFromBlockNumber: block.number }); } @@ -832,7 +833,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // otherwise the popped index operatorId will replace the deregistered operator's index uint32 operatorIndex = IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); uint32 quorumCountBefore = indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; - + assertTrue(operatorIndex <= quorumCountBefore - 1, "operator index should be less than quorumCount"); bytes32 operatorIdAtBeforeQuorumCount = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorumNumber, @@ -899,7 +900,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // otherwise the popped index operatorId will replace the deregistered operator's index uint32 operatorIndex = IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); uint32 quorumCountBefore = indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; - + assertTrue(operatorIndex <= quorumCountBefore - 1, "operator index should be less than quorumCount"); bytes32 operatorIdAtBeforeQuorumCount = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorumNumber, @@ -933,8 +934,8 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { /** * @dev Test deregistering an operator with multiple operators already registered. * We deregister the operator with arrayIndex 0 and check that the operator with arrayIndex 2 - * ends up getting swapped with the deregistering operator. - * + * ends up getting swapped with the deregistering operator. + * * Also checking QuorumUpdates and OperatorUpdates as well. */ function test_deregisterOperator_MultipleQuorums() public { @@ -984,7 +985,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { _assertQuorumUpdate({ quorumNumber: uint8(quorumsToRemove[i]), - expectedNumOperators: 2, + expectedNumOperators: 2, expectedFromBlockNumber: block.number }); @@ -1012,7 +1013,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // mask out quorums that are already initialized uint192 bitmap = uint192(bitmapToRegister.minus(uint256(initializedQuorumBitmap))); _initializeFuzzedQuorums(bitmap); - + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmapToRegister); bytes memory quorumsToRemove = bitmapUtilsWrapper.bitmapToBytesArray(bitmapToDeregister); @@ -1028,7 +1029,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators for removed quorums _assertQuorumUpdate({ quorumNumber: uint8(quorumsToRemove[i]), - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); // Check swapped operator's index for removed quorums diff --git a/test/unit/LibMergeSort.t.sol b/test/unit/LibMergeSort.t.sol new file mode 100644 index 00000000..dc2ddf7d --- /dev/null +++ b/test/unit/LibMergeSort.t.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "forge-std/Test.sol"; +import "../../src/libraries/LibMergeSort.sol"; + +contract LibMergeSortTest is Test { + using LibMergeSort for address[]; + + function testMergeSortArrays() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](6); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x5); + expected[5] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArraysWithDuplicates() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x1); + right[1] = address(0x3); + right[2] = address(0x5); + + address[] memory expected = new address[](3); + expected[0] = address(0x1); + expected[1] = address(0x3); + expected[2] = address(0x5); + + address[] memory result = left.mergeSortArrays(right); + assertEq(expected, result, "Not sorted"); + } + + function testMergeSortArraysWithEmptyLeft() public { + address[] memory left = new address[](0); + address[] memory right = new address[](3); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](3); + expected[0] = address(0x2); + expected[1] = address(0x4); + expected[2] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArraysWithEmptyRight() public { + address[] memory left = new address[](3); + address[] memory right = new address[](0); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + address[] memory expected = new address[](3); + expected[0] = address(0x1); + expected[1] = address(0x3); + expected[2] = address(0x5); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + +function testMergeSortArrays_Sort() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x3); + left[1] = address(0x1); + left[2] = address(0x2); + + right[0] = address(0x6); + right[1] = address(0x4); + right[2] = address(0x5); + + left = left.sort(); + right = right.sort(); + + address[] memory expected = new address[](6); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x5); + expected[5] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } +} + +/// NOTE: we're assuming the input arrays themselves are unique. +/// Demonstrating behavior of library +function testMergeSortArraysWithDuplicateInLeft() public { + address[] memory left = new address[](4); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x3); // Duplicate + left[3] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](7); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x3); + expected[4] = address(0x4); + expected[5] = address(0x5); + expected[6] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } +} +function testMergeSortArraysWithDuplicateInRight() public { + address[] memory left = new address[](3); + address[] memory right = new address[](4); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x4); // Duplicate + right[3] = address(0x6); + + address[] memory expected = new address[](7); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x4); + expected[5] = address(0x5); + expected[6] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } +} + + +} diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index ba4fe75e..55f6aabd 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; +import {IStakeRegistryErrors} from "../../src/interfaces/IStakeRegistry.sol"; contract OperatorStateRetrieverUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -13,7 +14,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { function test_getOperatorState_revert_neverRegistered() public { cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getOperatorState( registryCoordinator, defaultOperatorId, uint32(block.number) @@ -26,7 +27,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { // should revert because the operator was registered for the first time after the reference block number cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getOperatorState( registryCoordinator, defaultOperatorId, registrationBlockNumber - 1 @@ -93,7 +94,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); cheats.prank(registryCoordinator.owner()); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); cheats.expectRevert( "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" @@ -143,7 +144,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { nonSignerOperatorIds[0] = defaultOperatorId; cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, @@ -164,7 +165,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { // should revert because the operator was registered for the first time after the reference block number cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, @@ -186,9 +187,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { registryCoordinator.deregisterOperator(BitmapUtils.bitmapToBytesArray(1)); // should revert because the operator was registered for the first time after the reference block number - cheats.expectRevert( - "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber" - ); + cheats.expectRevert(OperatorStateRetriever.OperatorNotRegistered.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, uint32(block.number), @@ -203,7 +202,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { _registerOperatorWithCoordinator(defaultOperator, 1, defaultPubKey); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, uint32(block.number), @@ -235,11 +234,9 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); cheats.prank(registryCoordinator.owner()); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); - cheats.expectRevert( - "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" - ); + cheats.expectRevert(IStakeRegistryErrors.EmptyStakeHistory.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, registrationBlockNumber + 5, diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index c262b0da..72207dcc 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; +import {IRegistryCoordinatorErrors} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; contract RegistryCoordinatorUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -34,7 +37,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { // Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); @@ -72,7 +75,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { for (uint i = 0; i < defaultMaxOperatorCount - 1; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -81,7 +84,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); bytes32 operatorToKickId; address operatorToKick; - + // register last operator before kick operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); { @@ -115,22 +118,23 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina for (uint i = 0; i < numQuorums; i++) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), + keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), keccak256(abi.encode(operatorSetParams[i])) ); } - // make sure the contract intializers are disabled + // make sure the contract initializers are disabled cheats.expectRevert(bytes("Initializable: contract is already initialized")); registryCoordinator.initialize( registryCoordinatorOwner, - churnApprover, - ejector, - pauserRegistry, - 0/*initialPausedStatus*/, - operatorSetParams, - new uint96[](0), - new IStakeRegistry.StrategyParams[][](0) + churnApprover, + ejector, + 0/*initialPausedStatus*/, + operatorSetParams, + new uint96[](0), + new IStakeRegistry.StrategyParams[][](0), + new StakeType[](0), + new uint32[](0) ); } @@ -197,7 +201,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina function test_updateSocket_revert_notRegistered() public { cheats.prank(defaultOperator); - cheats.expectRevert("RegistryCoordinator.updateSocket: operator is not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator.updateSocket("localhost:32004"); } @@ -208,7 +212,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(defaultOperator); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); } function test_createQuorum() public { @@ -216,7 +220,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina // this is necessary since the default setup already configures the max number of quorums, preventing adding more _deployMockEigenLayerAndAVS(0); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, @@ -235,7 +239,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSetParamsUpdated(quorumCountBefore, operatorSetParams); cheats.prank(registryCoordinatorOwner); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); uint8 quorumCountAfter = registryCoordinator.quorumCount(); assertEq(quorumCountAfter, quorumCountBefore + 1, "quorum count did not increase properly"); @@ -260,7 +264,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni registryCoordinator.pause(2 ** PAUSED_REGISTER_OPERATOR); cheats.startPrank(defaultOperator); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -268,7 +272,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); cheats.prank(defaultOperator); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -279,7 +283,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -292,7 +296,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -322,7 +326,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -330,7 +334,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -363,13 +367,13 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni for (uint i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), actualStake); - } + } for (uint i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); - } - + } + uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); @@ -379,7 +383,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -387,7 +391,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -430,7 +434,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -438,7 +442,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), updateBlockNumber: uint32(registrationBlockNumber), @@ -446,7 +450,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(nextRegistrationBlockNumber), @@ -470,19 +474,19 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } address operatorToRegister = _incrementAddress(defaultOperator, numOperators); BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); + cheats.expectRevert(bytes4(keccak256("MaxQuorumsReached()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -502,8 +506,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -512,7 +515,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); } @@ -522,7 +525,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbersTooLarge, defaultSocket, emptySig); } @@ -534,7 +537,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); } @@ -560,7 +563,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -568,7 +571,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -591,7 +594,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(pauser); registryCoordinator.pause(2 ** PAUSED_DEREGISTER_OPERATOR); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -600,7 +603,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -616,7 +619,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -633,9 +636,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -653,7 +656,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist emit log_named_uint("gasUsed", gasBefore - gasAfter); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -661,7 +664,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -687,9 +690,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist } cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); @@ -708,7 +711,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist emit log_named_uint("numQuorums", quorumNumbers.length); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -716,7 +719,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -747,9 +750,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist } cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - + registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); @@ -772,27 +775,27 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) - ); + ); } else { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) - ); + ); } // ensure that the operator's current quorum bitmap matches the expectation uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); // check that the quorum bitmap history is as expected assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(registrationQuorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -802,7 +805,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(expectedQuorumBitmap), updateBlockNumber: deregistrationBlockNumber, @@ -815,7 +818,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // @notice registers the max number of operators with fuzzed bitmaps and then deregisters a pseudorandom operator (from all of their quorums) function testFuzz_deregisterOperator_manyOperators(uint256 pseudoRandomNumber) public { uint32 numOperators = defaultMaxOperatorCount; - + uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; @@ -827,14 +830,14 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist } cheats.roll(registrationBlockNumber); - + bytes32[] memory lastOperatorInQuorum = new bytes32[](numQuorums); for (uint i = 0; i < numOperators; i++) { emit log_named_uint("i", i); BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); bytes32 operatorId = BN254.hashG1Point(pubKey); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmaps[i], pubKey); // for each quorum the operator is in, save the operatorId @@ -858,7 +861,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(operatorToDeregister, operatorToDeregisterId, operatorToDeregisterQuorumNumbers); - + for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(operatorToDeregisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); @@ -870,7 +873,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), + keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToDeregisterId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -878,7 +881,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -898,9 +901,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(defaultOperator); - + cheats.roll(reregistrationBlockNumber); - + // store data before registering, to check against later IRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); @@ -911,7 +914,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId, "1"); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -921,14 +924,14 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap, "3"); // check that previous entry in bitmap history was not changed assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(previousQuorumBitmapUpdate)), "4" ); // check that new entry in bitmap history is as expected uint historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(reregistrationBlockNumber), @@ -956,13 +959,13 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist bytes memory emptyQuorumNumbers = new bytes(0); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: bitmap cannot be 0"); + cheats.expectRevert(bytes4(keccak256("BitmapCannotBeZero()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } function test_deregisterOperatorExternal_revert_notRegistered() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } @@ -984,7 +987,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist incorrectQuorum[0] = bytes1(defaultQuorumNumber + 1); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, incorrectQuorum); } @@ -1012,7 +1015,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); cheats.roll(reregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator cannot reregister yet"); + cheats.expectRevert(bytes4(keccak256("CannotReregisterYet()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -1087,27 +1090,27 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) - ); + ); } else { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) - ); + ); } // ensure that the operator's current quorum bitmap matches the expectation uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); // check that the quorum bitmap history is as expected assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(registrationQuorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -1117,7 +1120,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(expectedQuorumBitmap), updateBlockNumber: deregistrationBlockNumber, @@ -1147,10 +1150,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // eject cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); - + // make sure the operator is deregistered assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -1158,7 +1161,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); // make sure the operator is not in any quorums assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); - } + } function test_ejectOperator_subsetOfQuorums() public { // register operator with default stake with 2 quorums @@ -1186,10 +1189,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject); - + // make sure the operator is registered assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -1212,8 +1215,8 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); - - cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); + + cheats.expectRevert(bytes4(keccak256("OnlyEjector()"))); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } @@ -1221,7 +1224,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist function test_getQuorumBitmapIndicesAtBlockNumber_revert_notRegistered() public { uint32 blockNumber; bytes32[] memory operatorIds = new bytes32[](1); - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); + cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); } @@ -1234,7 +1237,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber); _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.roll(registrationBlockNumber); - cheats.startPrank(defaultOperator); + cheats.startPrank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint32 blockNumber = 0; @@ -1242,12 +1245,12 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist operatorIds[0] = defaultOperatorId; uint32[] memory returnArray; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); + cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); blockNumber = registrationBlockNumber + 1; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); @@ -1264,12 +1267,12 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist operatorIds[0] = defaultOperatorId; uint32[] memory returnArray; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); + cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); blockNumber = registrationBlockNumber + 1; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); @@ -1277,11 +1280,11 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist blockNumber = deregistrationBlockNumber; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1"); + assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1"); blockNumber = deregistrationBlockNumber + 1; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1"); + assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1"); } // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters @@ -1297,12 +1300,12 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist uint192 emptyBitmap = 0; // try an incorrect blockNumber input and confirm reversion - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); uint192 returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = registrationBlockNumber; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap"); + assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap"); blockNumber = registrationBlockNumber + 1; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); @@ -1310,20 +1313,20 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // try an incorrect index input and confirm reversion index = 1; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = deregistrationBlockNumber; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap"); + assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap"); blockNumber = deregistrationBlockNumber + 1; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap"); + assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap"); // try an incorrect index input and confirm reversion index = 0; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.NextBitmapUpdateIsBeforeBlockNumber.selector); returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); } } @@ -1345,7 +1348,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord for (uint i = 0; i < numOperators - 1; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1354,7 +1357,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); bytes32 operatorToKickId; address operatorToKick; - + // register last operator before kick IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); { @@ -1409,10 +1412,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); @@ -1421,21 +1424,21 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord } assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), + keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToRegisterId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), + keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToKickId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: kickRegistrationBlockNumber, @@ -1449,8 +1452,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); @@ -1462,12 +1465,12 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); + cheats.expectRevert(bytes4(keccak256("InsufficientStakeForChurn()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); @@ -1479,8 +1482,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); @@ -1494,12 +1497,12 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); + cheats.expectRevert(bytes4(keccak256("CannotKickOperatorAboveThreshold()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); @@ -1510,8 +1513,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, , IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); @@ -1526,12 +1529,12 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B"; signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); - cheats.expectRevert("ECDSA: invalid signature"); + cheats.expectRevert(bytes4(keccak256("InvalidSignature()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig ); @@ -1542,8 +1545,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); @@ -1556,12 +1559,12 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); + cheats.expectRevert(bytes4(keccak256("SignatureExpired()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig ); @@ -1576,7 +1579,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit address[] memory operatorsToUpdate = new address[](1); operatorsToUpdate[0] = defaultOperator; - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.updateOperators(operatorsToUpdate); } @@ -1611,7 +1614,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit uint32 registrationBlockNumber = 100; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(registrationBitmap); for (uint256 i = 0; i < quorumNumbers.length; ++i) { - _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); + _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); } cheats.startPrank(defaultOperator); cheats.roll(registrationBlockNumber); @@ -1627,7 +1630,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit uint192 quorumBitmapToRemove = mockReturnData; bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumBitmapToRemove); for (uint256 i = 0; i < quorumNumbersToRemove.length; ++i) { - _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); + _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); } uint256 expectedQuorumBitmap = BitmapUtils.minus(quorumBitmapBefore, quorumBitmapToRemove); @@ -1657,7 +1660,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1668,7 +1671,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit address[][] memory operatorsToUpdate = new address[][](1); cheats.prank(defaultOperator); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbersNotCreated); } @@ -1677,7 +1680,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: input length mismatch")); + cheats.expectRevert(bytes4(keccak256("InputLengthMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1689,7 +1692,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total")); + cheats.expectRevert(bytes4(keccak256("QuorumOperatorCountMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1710,7 +1713,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorArray[0] = _incrementAddress(defaultOperator, 1); operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum")); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1726,7 +1729,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1738,7 +1741,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorsToUpdate[0] = operatorArray; // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1753,7 +1756,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1764,7 +1767,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorArray[1] = defaultOperator; operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1806,7 +1809,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1832,7 +1835,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit function testFuzz_updateOperatorBitmapInternal_noPreviousEntries(uint192 newBitmap) public { registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(newBitmap), updateBlockNumber: uint32(block.number), @@ -1848,7 +1851,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(newBitmap), updateBlockNumber: uint32(block.number), @@ -1868,7 +1871,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(pastBitmap), updateBlockNumber: uint32(previousBlockNumber), @@ -1876,7 +1879,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(newBitmap), updateBlockNumber: uint32(block.number), @@ -1885,3 +1888,540 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit ); } } + +contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnitTests { + function test_registerALMHook_Reverts() public { + cheats.prank(address(serviceManager.allocationManager())); + cheats.expectRevert(); + registryCoordinator.registerOperator(defaultOperator, new uint32[](0), abi.encode(defaultSocket, pubkeyRegistrationParams)); + } + + function test_deregisterALMHook_Reverts() public { + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + cheats.prank(address(serviceManager.allocationManager())); + cheats.expectRevert(); + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + } + + function test_CreateTotalDelegatedStakeQuorum() public { + _deployMockEigenLayerAndAVS(0); + // Set up test params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(0x1)), + multiplier: 1000 + }); + + // Get initial quorum count + uint8 initialQuorumCount = registryCoordinator.quorumCount(); + + // Create quorum with total delegated stake type + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams + ); + + // Verify quorum was created + assertEq(registryCoordinator.quorumCount(), initialQuorumCount + 1); + + // Verify quorum params were set correctly + IRegistryCoordinator.OperatorSetParam memory storedParams = registryCoordinator.getOperatorSetParams(initialQuorumCount); + assertEq(storedParams.maxOperatorCount, operatorSetParams.maxOperatorCount); + assertEq(storedParams.kickBIPsOfOperatorStake, operatorSetParams.kickBIPsOfOperatorStake); + assertEq(storedParams.kickBIPsOfTotalStake, operatorSetParams.kickBIPsOfTotalStake); + } + + function test_CreateSlashableStakeQuorum_Reverts() public { + _deployMockEigenLayerAndAVS(0); + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(0x1)), + multiplier: 1000 + }); + uint32 lookAheadPeriod = 100; + + // Attempt to create quorum with slashable stake type before enabling operator sets + cheats.prank(registryCoordinatorOwner); + cheats.expectRevert(); + registryCoordinator.createSlashableStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams, + lookAheadPeriod + ); + } + + function test_MigrateToOperatorSets() public { + _deployMockEigenLayerAndAVS(0); + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + assertTrue(registryCoordinator.isUsingOperatorSets()); + } +} + +contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitTests { + function test_MigrateToOperatorSets() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + assertTrue(registryCoordinator.isUsingOperatorSets()); + } + + function test_M2_Deregister() public { + vm.skip(true); + /// Create 2 M2 quorums + _deployMockEigenLayerAndAVS(2); + + address operatorToRegister = address(420); + + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignature = ISignatureUtils.SignatureWithSaltAndExpiry({ + signature: new bytes(0), + salt: bytes32(0), + expiry: 0 + }); + + IBLSApkRegistry.PubkeyRegistrationParams memory operatorRegisterApkParams = IBLSApkRegistry.PubkeyRegistrationParams({ + pubkeyRegistrationSignature: BN254.G1Point({ + X: 0, + Y: 0 + }), + pubkeyG1: BN254.G1Point({ + X: 0, + Y: 0 + }), + pubkeyG2: BN254.G2Point({ + X: [uint256(0), uint256(0)], + Y: [uint256(0), uint256(0)] + }) + }); + + string memory socket = "socket"; + + // register for quorum 0 + vm.prank(operatorToRegister); + registryCoordinator.registerOperator( + new bytes(1), // Convert 0 to bytes1 first + socket, + operatorRegisterApkParams, + emptySignature + ); + + /// migrate to operator sets + registryCoordinator.enableOperatorSets(); + + /// Deregistration for m2 should for the first two operator sets + vm.prank(defaultOperator); + registryCoordinator.deregisterOperator(new bytes(1)); + + // Verify operator was deregistered by checking their bitmap is empty + bytes32 operatorId = registryCoordinator.getOperatorId(operatorToRegister); + uint192 bitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); + assertEq(bitmap, 0, "Operator bitmap should be empty after deregistration"); + + // Verify operator status is NEVER_REGISTERED + IRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(operatorToRegister); + assertEq(uint8(status), uint8(IRegistryCoordinator.OperatorStatus.NEVER_REGISTERED), "Operator status should be NEVER_REGISTERED"); + } + + function test_M2_Register_Reverts() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(0)); + IBLSApkRegistry.PubkeyRegistrationParams memory params; + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + cheats.expectRevert(); + registryCoordinator.registerOperator( + quorumNumbers, + defaultSocket, + params, + operatorSignature + ); + } + + function test_createSlashableStakeQuorum() public { + // Deploy with 0 quorums + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 1 + }); + uint32 lookAheadPeriod = 100; + + // Create slashable stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createSlashableStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams, + lookAheadPeriod + ); + } + + function test_createTotalDelegatedStakeQuorum() public { + // Deploy with 0 quorums + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams + ); + } + + function test_registerHook() public { + vm.skip(true); + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + address allocationManager = address(serviceManager.allocationManager()); + cheats.prank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + } + + function test_registerHook_WithChurn() public { + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); + operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ + operator: address(0x1), + quorumNumber: 0 + }); + + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature; + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + bytes memory registerParams = abi.encode( + socket, + params, + operatorKickParams, + churnApproverSignature, + operatorSignature + ); + + // Prank as allocation manager and call register hook + address allocationManager = address(serviceManager.allocationManager()); + cheats.prank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, registerParams); + } + + function test_updateStakesForQuorum() public { + vm.skip(true); + _deployMockEigenLayerAndAVS(0); + + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams + ); + + uint256 quorumBitmap = 0; + + // TODO: register actually and update stakes + } + + function test_deregisterHook() public { + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + // Prank as allocation manager and call register hook + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + + address allocationManager = address(serviceManager.allocationManager()); + cheats.startPrank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + + cheats.stopPrank(); + } + + function test_registerHook_Reverts_WhenNotALM() public { + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + vm.expectRevert(); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + } + + function test_deregisterHook_Reverts_WhenNotALM() public { + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + // Prank as allocation manager and call register hook + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + + address allocationManager = address(serviceManager.allocationManager()); + cheats.startPrank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + cheats.stopPrank(); + + cheats.expectRevert(); + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + + } + + function test_DeregisterHook_Reverts_WhenM2Quorum() public { + vm.skip(true); + } + + function test_registerHook_Reverts_WhenM2Quorum() public { + vm.skip(true); + } + +} diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index 2b04dabd..ca3d0e9f 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -1,14 +1,18 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import { RewardsCoordinator, IRewardsCoordinator, + IRewardsCoordinatorTypes, IERC20 } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; +import {IServiceManagerErrors} from "../../src/interfaces/IServiceManager.sol"; import "../utils/MockAVSDeployer.sol"; @@ -21,6 +25,9 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve uint32 MAX_FUTURE_LENGTH = 28 days; uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; + uint32 OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP = 0; /// TODO: what values should these have + uint32 OPERATOR_SET_MAX_RETROACTIVE_LENGTH = 0; /// TODO: What values these should have + /// @notice Delay in timestamp before a posted root can be claimed against uint32 activationDelay = 7 days; /// @notice the commission for all operators across all avss @@ -50,7 +57,10 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve // Deploy rewards coordinator rewardsCoordinatorImplementation = new RewardsCoordinator( delegationMock, - strategyManagerMock, + IStrategyManager(address(strategyManagerMock)), + allocationManagerMock, + pauserRegistry, + permissionControllerMock, CALCULATION_INTERVAL_SECONDS, MAX_REWARDS_DURATION, MAX_RETROACTIVE_LENGTH, @@ -66,7 +76,6 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve abi.encodeWithSelector( RewardsCoordinator.initialize.selector, msg.sender, - pauserRegistry, 0, /*initialPausedStatus*/ rewardsUpdater, activationDelay, @@ -80,7 +89,8 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve avsDirectory, rewardsCoordinator, registryCoordinatorImplementation, - stakeRegistryImplementation + stakeRegistryImplementation, + allocationManagerImplementation ); serviceManager = ServiceManagerMock( @@ -89,7 +99,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve address(serviceManagerImplementation), address(proxyAdmin), abi.encodeWithSelector( - ServiceManagerMock.initialize.selector, msg.sender, msg.sender + ServiceManagerMock.initialize.selector, serviceManager.owner(), msg.sender, msg.sender ) ) ) @@ -140,7 +150,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve IERC20 token3 = new ERC20PresetFixedSupply( "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) ); - strategyImplementation = new StrategyBase(strategyManagerMock); + strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock)), pauserRegistry); strategyMock1 = StrategyBase( address( new TransparentUpgradeableProxy( @@ -179,13 +189,13 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve strategyManagerMock.setStrategyWhitelist(strategies[2], true); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) ); } @@ -216,9 +226,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions; cheats.prank(caller); - cheats.expectRevert( - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + cheats.expectRevert(IServiceManagerErrors.OnlyRewardsInitiator.selector); serviceManager.createAVSRewardsSubmission(rewardsSubmissions); } @@ -227,9 +235,9 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve "dog wif hat", "MOCK1", mockTokenInitialSupply, rewardsInitiator ); - IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions = - new IRewardsCoordinator.RewardsSubmission[](1); - rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinatorTypes.RewardsSubmission[](1); + rewardsSubmissions[0] = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: token, amount: 100, @@ -242,7 +250,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve serviceManager.createAVSRewardsSubmission(rewardsSubmissions); } - function test_createAVSRewardsSubmission_SingleSubmission( + function testFuzz_createAVSRewardsSubmission_SingleSubmission( uint256 startTimestamp, uint256 duration, uint256 amount @@ -266,9 +274,9 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions = - new IRewardsCoordinator.RewardsSubmission[](1); - rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinatorTypes.RewardsSubmission[](1); + rewardsSubmissions[0] = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: rewardToken, amount: amount, @@ -319,7 +327,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve ); } - function test_createAVSRewardsSubmission_MultipleSubmissions( + function testFuzz_createAVSRewardsSubmission_MultipleSubmissions( uint256 startTimestamp, uint256 duration, uint256 amount, @@ -359,7 +367,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission memory rewardsSubmission = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission memory rewardsSubmission = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: rewardTokens[i], amount: amounts[i], @@ -412,7 +420,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve } } - function test_createAVSRewardsSubmission_MultipleSubmissionsSingleToken( + function testFuzz_createAVSRewardsSubmission_MultipleSubmissionsSingleToken( uint256 startTimestamp, uint256 duration, uint256 amount, @@ -457,7 +465,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission memory rewardsSubmission = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission memory rewardsSubmission = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: rewardToken, amount: amounts[i], @@ -524,4 +532,4 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve cheats.prank(caller); serviceManager.setRewardsInitiator(newRewardsInitiator); } -} +} \ No newline at end of file diff --git a/test/unit/ServiceManagerRouter.t.sol b/test/unit/ServiceManagerRouter.t.sol index 9fc2c0f7..5cf1d3b7 100644 --- a/test/unit/ServiceManagerRouter.t.sol +++ b/test/unit/ServiceManagerRouter.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../../src/ServiceManagerRouter.sol"; import "../utils/MockAVSDeployer.sol"; @@ -19,7 +19,8 @@ contract ServiceManagerRouter_UnitTests is MockAVSDeployer { avsDirectory, rewardsCoordinatorImplementation, registryCoordinatorImplementation, - stakeRegistryImplementation + stakeRegistryImplementation, + allocationManagerImplementation ); _registerOperatorWithCoordinator(defaultOperator, MAX_QUORUM_BITMAP, defaultPubKey); diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index ed53225c..a21a8c39 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "test/utils/MockAVSDeployer.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, IStakeRegistryErrors} from "src/interfaces/IStakeRegistry.sol"; import {IStakeRegistryEvents} from "test/events/IStakeRegistryEvents.sol"; import "../utils/MockAVSDeployer.sol"; @@ -48,11 +48,12 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { serviceManager, stakeRegistry, IBLSApkRegistry(blsApkRegistry), - IIndexRegistry(indexRegistry) + IIndexRegistry(indexRegistry), + pauserRegistry ); stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(address(registryCoordinator)), delegationMock + IRegistryCoordinator(address(registryCoordinator)), delegationMock, avsDirectoryMock, serviceManager ); stakeRegistry = StakeRegistryHarness( @@ -99,7 +100,10 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { nextQuorum++; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + + StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq(uint8(stakeType), uint8(StakeType.TOTAL_DELEGATED), "invalid stake type"); // Mark quorum initialized for other tests initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); @@ -126,7 +130,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { nextQuorum++; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); // Mark quorum initialized for other tests initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); @@ -576,10 +580,8 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) public { - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } function testFuzz_initializeQuorum_Revert_WhenQuorumAlreadyExists( @@ -587,9 +589,9 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert("StakeRegistry.initializeQuorum: quorum already exists"); + cheats.expectRevert(IStakeRegistryErrors.QuorumAlreadyExists.selector); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } function testFuzz_initializeQuorum_Revert_WhenInvalidArrayLengths( @@ -599,9 +601,9 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { cheats.assume(quorumNumber >= nextQuorum); IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](0); - cheats.expectRevert("StakeRegistry._addStrategyParams: no strategies provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); strategyParams = new IStakeRegistry.StrategyParams[](MAX_WEIGHING_FUNCTION_LENGTH + 1); for (uint256 i = 0; i < strategyParams.length; i++) { @@ -609,9 +611,57 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), uint96(1) ); } - cheats.expectRevert("StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthMismatch.selector); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + } + event StakeTypeSet(StakeType newStakeType); + + function test_initializeDelegatedStakeQuorum() public { + uint8 quorumNumber = nextQuorum; + uint96 minimumStake = 0; + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams( + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), + uint96(WEIGHTING_DIVISOR) + ); + + cheats.prank(address(registryCoordinator)); + cheats.expectEmit(true, true, true, true); + emit StakeTypeSet(StakeType.TOTAL_DELEGATED); + stakeRegistry.initializeDelegatedStakeQuorum( + quorumNumber, + minimumStake, + strategyParams + ); + + StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq(uint8(stakeType), uint8(StakeType.TOTAL_DELEGATED), "invalid stake type"); + } + + function test_initializeSlashableStakeQuorum() public { + uint8 quorumNumber = nextQuorum; + uint96 minimumStake = 0; + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams( + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), + uint96(WEIGHTING_DIVISOR) + ); + + cheats.prank(address(registryCoordinator)); + cheats.expectEmit(true, true, true, true); + emit StakeTypeSet(StakeType.TOTAL_SLASHABLE); + stakeRegistry.initializeSlashableStakeQuorum( + quorumNumber, + minimumStake, + 7 days, + strategyParams + ); + + StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq(uint8(stakeType), uint8(StakeType.TOTAL_SLASHABLE), "invalid stake type"); } /** @@ -635,7 +685,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { } quorumNumber = nextQuorum; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); IStakeRegistry.StakeUpdate memory initialStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, 0); @@ -679,9 +729,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint96 minimumStakeForQuorum ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -691,7 +739,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -719,9 +767,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, IStakeRegistry.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -731,7 +777,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -746,7 +792,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { strategyParams[0] = IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); strategyParams[1] = IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); - cheats.expectRevert("StakeRegistry._addStrategyParams: cannot add same strategy 2x"); + cheats.expectRevert(IStakeRegistryErrors.InputDuplicateStrategy.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -760,9 +806,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { new IStakeRegistry.StrategyParams[](2); strategyParams[0] = IStakeRegistry.StrategyParams(strat, 0); - cheats.expectRevert( - "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" - ); + cheats.expectRevert(IStakeRegistryErrors.InputMultiplierZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -822,9 +866,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint256[] memory indicesToRemove ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -834,7 +876,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -864,7 +906,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber = _initializeQuorum(minimumStake, numStrategiesToAdd); uint256[] memory indicesToRemove = new uint256[](0); - cheats.expectRevert("StakeRegistry.removeStrategies: no indices to remove provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -925,9 +967,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint256[] calldata strategyIndices, uint96[] calldata newMultipliers ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -938,7 +978,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -949,7 +989,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { { uint256[] memory strategyIndices = new uint256[](0); uint96[] memory newMultipliers = new uint96[](0); - cheats.expectRevert("StakeRegistry.modifyStrategyParams: no strategy indices provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -961,7 +1001,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public fuzzOnlyInitializedQuorums(quorumNumber) { cheats.assume(strategyIndices.length != newMultipliers.length); cheats.assume(strategyIndices.length > 0); - cheats.expectRevert("StakeRegistry.modifyStrategyParams: input length mismatch"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthMismatch.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -1020,9 +1060,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { function test_registerOperator_Revert_WhenNotRegistryCoordinator() public { (address operator, bytes32 operatorId) = _selectNewOperator(); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); stakeRegistry.registerOperator(operator, operatorId, initializedQuorumBytes); } @@ -1032,7 +1070,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } @@ -1066,9 +1104,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { } // Attempt to register - cheats.expectRevert( - "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" - ); + cheats.expectRevert(IStakeRegistryErrors.BelowMinimumStakeRequirement.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); } @@ -1374,9 +1410,7 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { fuzzy_addtlStake: 0 }); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); } @@ -1391,7 +1425,7 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } @@ -1738,9 +1772,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({registeredFor: initializedQuorumBitmap, fuzzy_Delta: 0}); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, setup.quorumNumbers); } @@ -1752,7 +1784,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, invalidQuorums); } @@ -2119,7 +2151,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe * successfully and return a value for weightOfOperatorForQuorum. Fuzz test sets the operator shares * and asserts that the summed weight of the operator is correct. */ - function test_weightOfOperatorForQuorum( + function testFuzz_weightOfOperatorForQuorum( address operator, uint96[] memory multipliers, uint96[] memory shares @@ -2150,7 +2182,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe } cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); // set the operator shares for (uint256 i = 0; i < strategyParams.length; i++) { @@ -2174,7 +2206,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe } /// @dev consider multipliers for 3 strategies - function test_weightOfOperatorForQuorum_3Strategies( + function testFuzz_weightOfOperatorForQuorum_3Strategies( address operator, uint96[3] memory shares ) public { @@ -2197,7 +2229,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe // create a valid quorum cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); // set the operator shares for (uint256 i = 0; i < strategyParams.length; i++) { diff --git a/test/unit/UpgradeableProxyLib.sol b/test/unit/UpgradeableProxyLib.sol new file mode 100644 index 00000000..15fd49d8 --- /dev/null +++ b/test/unit/UpgradeableProxyLib.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; +import {Vm} from "forge-std/Vm.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +contract EmptyContract { +} + +library UpgradeableProxyLib { + bytes32 internal constant IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 internal constant ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + function deployProxyAdmin() internal returns (address) { + return address(new ProxyAdmin()); + } + function setUpEmptyProxy( + address admin + ) internal returns (address) { + address emptyContract = address(new EmptyContract()); + return address(new TransparentUpgradeableProxy(emptyContract, admin, "")); + } + function upgrade(address proxy, address impl) internal { + ProxyAdmin admin = getProxyAdmin(proxy); + admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), impl); + } + function upgradeAndCall(address proxy, address impl, bytes memory initData) internal { + ProxyAdmin admin = getProxyAdmin(proxy); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), impl, initData); + } + function getImplementation( + address proxy + ) internal view returns (address) { + bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT); + return address(uint160(uint256(value))); + } + function getProxyAdmin( + address proxy + ) internal view returns (ProxyAdmin) { + bytes32 value = vm.load(proxy, ADMIN_SLOT); + return ProxyAdmin(address(uint160(uint256(value)))); + } +} \ No newline at end of file diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index 0947b1d4..6e085215 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; @@ -7,7 +7,7 @@ contract Utils { address constant dummyAdmin = address(uint160(uint256(keccak256("DummyAdmin")))); function deployNewStrategy(IERC20 token, IStrategyManager strategyManager, IPauserRegistry pauserRegistry, address admin) public returns (StrategyBase) { - StrategyBase newStrategy = new StrategyBase(strategyManager); + StrategyBase newStrategy = new StrategyBase(strategyManager, pauserRegistry); newStrategy = StrategyBase( address( new TransparentUpgradeableProxy( @@ -17,7 +17,7 @@ contract Utils { ) ) ); - newStrategy.initialize(token, pauserRegistry); + newStrategy.initialize(token); return newStrategy; } } diff --git a/test/unit/ZeusDeploymentTests.t.sol b/test/unit/ZeusDeploymentTests.t.sol new file mode 100644 index 00000000..b9970ff2 --- /dev/null +++ b/test/unit/ZeusDeploymentTests.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Test, console2 as console} from "forge-std/Test.sol"; +import {CoreDeploymentLib } from "../../script/utils/CoreDeploymentLib.sol"; + +contract ZeusDeploymentTests is Test { + using CoreDeploymentLib for string; + + function testParseZeusJson() public { + string memory path = "script/config"; + uint256 chainId = 17000; + string memory environment = "preprod"; + + CoreDeploymentLib.DeploymentData memory deploymentData = CoreDeploymentLib.readCoreDeploymentJson(path, chainId, environment); + + assertEq(deploymentData.delegationManager, 0x75dfE5B44C2E530568001400D3f704bC8AE350CC); + assertEq(deploymentData.avsDirectory, 0x141d6995556135D4997b2ff72EB443Be300353bC); + assertEq(deploymentData.allocationManager, 0xFdD5749e11977D60850E06bF5B13221Ad95eb6B4); + assertEq(deploymentData.strategyManager, 0xF9fbF2e35D8803273E214c99BF15174139f4E67a); + assertEq(deploymentData.eigenPodManager, 0xB8d8952f572e67B11e43bC21250967772fa883Ff); + assertEq(deploymentData.rewardsCoordinator, 0xb22Ef643e1E067c994019A4C19e403253C05c2B0); + assertEq(deploymentData.eigenPodBeacon, 0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC); + assertEq(deploymentData.pauserRegistry, 0x50712285cE831a6B9a11214A430f28999A5b4DAe); + assertEq(deploymentData.strategyFactory, 0xad4A89E3cA9b3dc25AABe0aa7d72E61D2Ec66052); + assertEq(deploymentData.strategyBeacon, 0xf2c2AcA859C685895E60ca7A14274365b64c0c2a); + assertEq(deploymentData.eigenStrategy, 0x4e0125f8a928Eb1b9dB4BeDd3756BA3c200563C2); + assertEq(deploymentData.eigen, 0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926); + assertEq(deploymentData.backingEigen, 0xA72942289a043874249E60469F68f08B8c6ECCe8); + assertEq(deploymentData.permissionController, 0xa2348c77802238Db39f0CefAa500B62D3FDD682b); + } +} diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 3f5286a3..7923fca4 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; @@ -130,8 +130,8 @@ contract BLSMockAVSDeployer is MockAVSDeployer { OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, - referenceBlockNumber, - quorumNumbers, + referenceBlockNumber, + quorumNumbers, nonSignerOperatorIds ); diff --git a/test/utils/CoreDeployLib.sol b/test/utils/CoreDeployLib.sol new file mode 100644 index 00000000..d88f6ff6 --- /dev/null +++ b/test/utils/CoreDeployLib.sol @@ -0,0 +1,271 @@ +// // SPDX-License-Identifier: UNLICENSED +// pragma solidity ^0.8.0; + +// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +// import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +// import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; +// import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +// import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; +// import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +// import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +// import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; +// import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; +// import {StrategyBaseTVLLimits} from "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol"; +// import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +// import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +// import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +// import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +// import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +// import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +// import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; +// import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +// import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +// import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; + +// import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; + +// library CoreDeploymentLib { +// using UpgradeableProxyLib for address; + +// struct StrategyManagerConfig { +// uint256 initPausedStatus; +// uint256 initWithdrawalDelayBlocks; +// } + +// struct DelegationManagerConfig { +// uint256 initPausedStatus; +// IStrategy[] strategies; +// uint256 minWithdrawalDelayBlocks; +// uint256[] withdrawalDelayBlocks; + +// } + +// struct EigenPodManagerConfig { +// uint256 initPausedStatus; +// } + +// struct RewardsCoordinatorConfig { +// uint256 initPausedStatus; +// uint256 maxRewardsDuration; +// uint256 maxRetroactiveLength; +// uint256 maxFutureLength; +// uint256 genesisRewardsTimestamp; +// address updater; +// uint256 activationDelay; +// uint256 calculationIntervalSeconds; +// uint256 globalOperatorCommissionBips; +// } + +// struct StrategyFactoryConfig { +// uint256 initPausedStatus; +// } + +// struct DeploymentConfigData { +// StrategyManagerConfig strategyManager; +// DelegationManagerConfig delegationManager; +// EigenPodManagerConfig eigenPodManager; +// RewardsCoordinatorConfig rewardsCoordinator; +// StrategyFactoryConfig strategyFactory; +// } + +// struct DeploymentData { +// address delegationManager; +// address avsDirectory; +// address strategyManager; +// address eigenPodManager; +// address rewardsCoordinator; +// address eigenPodBeacon; +// address pauserRegistry; +// address strategyFactory; +// address strategyBeacon; +// } + +// function deployContracts( +// address proxyAdmin, +// DeploymentConfigData memory configData +// ) internal returns (DeploymentData memory) { +// DeploymentData memory result; + +// result.delegationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.avsDirectory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.strategyManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.eigenPodManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.rewardsCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.eigenPodBeacon = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.pauserRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.strategyFactory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + +// // Deploy the implementation contracts, using the proxy contracts as inputs +// address delegationManagerImpl = address( +// new DelegationManager( +// IStrategyManager(result.strategyManager), +// IEigenPodManager(result.eigenPodManager) +// ) +// ); +// address avsDirectoryImpl = +// address(new AVSDirectory(IDelegationManager(result.delegationManager))); + +// address strategyManagerImpl = address( +// new StrategyManager( +// IDelegationManager(result.delegationManager), +// IEigenPodManager(result.eigenPodManager) +// ) +// ); + +// address strategyFactoryImpl = +// address(new StrategyFactory(IStrategyManager(result.strategyManager))); + +// address ethPOSDeposit; +// if (block.chainid == 1) { +// ethPOSDeposit = 0x00000000219ab540356cBB839Cbe05303d7705Fa; +// } else { +// // For non-mainnet chains, you might want to deploy a mock or read from a config +// // This assumes you have a similar config setup as in M2_Deploy_From_Scratch.s.sol +// /// TODO: Handle Eth pos +// } + +// address eigenPodManagerImpl = address( +// new EigenPodManager( +// IETHPOSDeposit(ethPOSDeposit), +// IBeacon(result.eigenPodBeacon), +// IStrategyManager(result.strategyManager), +// IDelegationManager(result.delegationManager) +// ) +// ); + +// /// TODO: Get actual values +// uint32 CALCULATION_INTERVAL_SECONDS = 1 days; +// uint32 MAX_REWARDS_DURATION = 1 days; +// uint32 MAX_RETROACTIVE_LENGTH = 1; +// uint32 MAX_FUTURE_LENGTH = 1; +// uint32 GENESIS_REWARDS_TIMESTAMP = 10 days; +// address rewardsCoordinatorImpl = address( +// new RewardsCoordinator( +// IDelegationManager(result.delegationManager), +// IStrategyManager(result.strategyManager), +// CALCULATION_INTERVAL_SECONDS, +// MAX_REWARDS_DURATION, +// MAX_RETROACTIVE_LENGTH, +// MAX_FUTURE_LENGTH, +// GENESIS_REWARDS_TIMESTAMP +// ) +// ); + +// /// TODO: Get actual genesis time +// uint64 GENESIS_TIME = 1_564_000; + +// address eigenPodImpl = address( +// new EigenPod( +// IETHPOSDeposit(ethPOSDeposit), +// IEigenPodManager(result.eigenPodManager), +// GENESIS_TIME +// ) +// ); +// address eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); +// address baseStrategyImpl = +// address(new StrategyBase(IStrategyManager(result.strategyManager))); +// /// TODO: PauserRegistry isn't upgradeable +// address pauserRegistryImpl = address( +// new PauserRegistry( +// new address[](0), // Empty array for pausers +// proxyAdmin // ProxyAdmin as the unpauser +// ) +// ); + +// // Deploy and configure the strategy beacon +// result.strategyBeacon = address(new UpgradeableBeacon(baseStrategyImpl)); + +// // Upgrade contracts +// /// TODO: Get from config +// bytes memory upgradeCall = abi.encodeWithSelector( /// TODO: Fix abi.encodeCall was failing Cannot implicitly convert component at position 4 from "IStrategy[]" to "IStrategy[]" +// DelegationManager.initialize.selector, +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.delegationManager.initPausedStatus, // initialPausedStatus +// configData.delegationManager.minWithdrawalDelayBlocks, // _minWithdrawalDelayBlocks +// configData.delegationManager.strategies, // _strategies +// configData.delegationManager.withdrawalDelayBlocks // _withdrawalDelayBlocks +// ); +// UpgradeableProxyLib.upgradeAndCall( +// result.delegationManager, delegationManagerImpl, upgradeCall +// ); + +// // Upgrade StrategyManager contract +// upgradeCall = abi.encodeCall( +// StrategyManager.initialize, +// ( +// proxyAdmin, // initialOwner +// result.strategyFactory, // initialStrategyWhitelister +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.strategyManager.initPausedStatus // initialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.strategyManager, strategyManagerImpl, upgradeCall); + +// // Upgrade StrategyFactory contract +// upgradeCall = abi.encodeCall( +// StrategyFactory.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.strategyFactory.initPausedStatus, // initialPausedStatus +// IBeacon(result.strategyBeacon) +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, strategyFactoryImpl, upgradeCall); + +// // Upgrade EigenPodManager contract +// upgradeCall = abi.encodeCall( +// EigenPodManager.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.eigenPodManager.initPausedStatus // initialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, eigenPodManagerImpl, upgradeCall); + +// // Upgrade AVSDirectory contract +// upgradeCall = abi.encodeCall( +// AVSDirectory.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// 0 // TODO: AVS Missing configinitialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, avsDirectoryImpl, upgradeCall); + +// // Upgrade RewardsCoordinator contract +// upgradeCall = abi.encodeCall( +// RewardsCoordinator.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.rewardsCoordinator.initPausedStatus, // initialPausedStatus +// /// TODO: is there a setter and is this expected? +// address(0), // rewards updater +// uint32(configData.rewardsCoordinator.activationDelay), // _activationDelay +// uint16(configData.rewardsCoordinator.globalOperatorCommissionBips) // _globalCommissionBips +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall( +// result.rewardsCoordinator, rewardsCoordinatorImpl, upgradeCall +// ); + +// // Upgrade EigenPod contract +// upgradeCall = abi.encodeCall( +// EigenPod.initialize, +// // TODO: Double check this +// (address(result.eigenPodManager)) // _podOwner +// ); +// UpgradeableProxyLib.upgradeAndCall(result.eigenPodBeacon, eigenPodImpl, upgradeCall); + +// return result; +// } + +// } \ No newline at end of file diff --git a/test/utils/Greeter.sol b/test/utils/Greeter.sol deleted file mode 100644 index 35948a85..00000000 --- a/test/utils/Greeter.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract Greeter { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } -} - diff --git a/test/utils/GreeterProxiable.sol b/test/utils/GreeterProxiable.sol deleted file mode 100644 index e6a6e17c..00000000 --- a/test/utils/GreeterProxiable.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -interface IERC1822Proxiable { - function proxiableUUID() external view returns (bytes32); -} - -contract Proxiable { - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; - - function upgradeToAndCall(address newImplementation, bytes calldata data) external { - try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { - if (slot != _IMPLEMENTATION_SLOT) { - revert("slot is unsupported as a uuid"); - } - _setImplementation(newImplementation); - if (data.length > 0) { - /** - * Note that using delegate call can make your implementation contract vulnerable if this function - * is not protected with the `onlyProxy` modifier. Again, this contract is for testing only, it is - * not safe for use in production. Instead, use the `UUPSUpgradeable` contract available in - * @openzeppelin/contracts-upgradeable - */ - /// @custom:oz-upgrades-unsafe-allow delegatecall - (bool success, ) = newImplementation.delegatecall(data); - require(success, "upgrade call reverted"); - } else { - _checkNonPayable(); - } - } catch { - revert("the implementation is not UUPS"); - } - } - - function proxiableUUID() external view virtual returns (bytes32) { - return _IMPLEMENTATION_SLOT; - } - - function _checkNonPayable() private { - if (msg.value > 0) { - revert("non-payable upgrade call"); - } - } - - function _setImplementation(address newImplementation) private { - bytes32 slot = _IMPLEMENTATION_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, newImplementation) - } - } -} - - -contract GreeterProxiable is Proxiable { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } -} - - diff --git a/test/utils/GreeterV2.sol b/test/utils/GreeterV2.sol deleted file mode 100644 index 15a2b9b8..00000000 --- a/test/utils/GreeterV2.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract GreeterV2 { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } - - function resetGreeting() public { - greeting = "resetted"; - } -} diff --git a/test/utils/GreeterV2Proxiable.sol b/test/utils/GreeterV2Proxiable.sol deleted file mode 100644 index ca2d82c9..00000000 --- a/test/utils/GreeterV2Proxiable.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Proxiable} from "./GreeterProxiable.sol"; - -contract GreeterV2Proxiable is Proxiable { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } - - function resetGreeting() public { - greeting = "resetted"; - } -} - diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index eb7a9b70..3a14c824 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; -import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; @@ -17,7 +15,7 @@ import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "../harnesses/RegistryCoordinatorHarness.t.sol"; import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; -import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {StakeRegistry, StakeType} from "../../src/StakeRegistry.sol"; import {IndexRegistry} from "../../src/IndexRegistry.sol"; import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; @@ -26,16 +24,21 @@ import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.so import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; -import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; +import {EigenPodManagerMock} from "../mocks/EigenPodManagerMock.sol"; import {AVSDirectoryMock} from "../mocks/AVSDirectoryMock.sol"; +import {AllocationManagerMock} from "../mocks/AllocationManagerMock.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {RewardsCoordinatorMock} from "../mocks/RewardsCoordinatorMock.sol"; +import {PermissionControllerMock} from "../mocks/PermissionControllerMock.sol"; -import { RewardsCoordinator } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; -import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {BLSApkRegistryHarness} from "../harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; @@ -52,9 +55,6 @@ contract MockAVSDeployer is Test { ProxyAdmin public proxyAdmin; PauserRegistry public pauserRegistry; - ISlasher public slasher = ISlasher(address(uint160(uint256(keccak256("slasher"))))); - Slasher public slasherImplementation; - EmptyContract public emptyContract; RegistryCoordinatorHarness public registryCoordinatorImplementation; @@ -76,9 +76,13 @@ contract MockAVSDeployer is Test { AVSDirectory public avsDirectory; AVSDirectory public avsDirectoryImplementation; AVSDirectoryMock public avsDirectoryMock; + AllocationManagerMock public allocationManagerMock; + AllocationManager public allocationManager; + AllocationManager public allocationManagerImplementation; RewardsCoordinator public rewardsCoordinator; RewardsCoordinator public rewardsCoordinatorImplementation; RewardsCoordinatorMock public rewardsCoordinatorMock; + PermissionControllerMock public permissionControllerMock; /// @notice StakeRegistry, Constant used as a divisor in calculating weights. uint256 public constant WEIGHTING_DIVISOR = 1e18; @@ -135,37 +139,20 @@ contract MockAVSDeployer is Test { function _deployMockEigenLayerAndAVS(uint8 numQuorumsToAdd) internal { emptyContract = new EmptyContract(); - defaultOperatorId = defaultPubKey.hashG1Point(); cheats.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); - address[] memory pausers = new address[](1); pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - delegationMock = new DelegationMock(); avsDirectoryMock = new AVSDirectoryMock(); eigenPodManagerMock = new EigenPodManagerMock(pauserRegistry); - strategyManagerMock = new StrategyManagerMock(); - slasherImplementation = new Slasher(strategyManagerMock, delegationMock); - slasher = Slasher( - address( - new TransparentUpgradeableProxy( - address(slasherImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - Slasher.initialize.selector, - msg.sender, - pauserRegistry, - 0 /*initialPausedStatus*/ - ) - ) - ) - ); - avsDirectoryMock = new AVSDirectoryMock(); - avsDirectoryImplementation = new AVSDirectory(delegationMock); + strategyManagerMock = new StrategyManagerMock(delegationMock); + allocationManagerMock = new AllocationManagerMock(); + permissionControllerMock = new PermissionControllerMock(); + avsDirectoryImplementation = new AVSDirectory(delegationMock, pauserRegistry); // TODO: config value avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -173,16 +160,14 @@ contract MockAVSDeployer is Test { address(proxyAdmin), abi.encodeWithSelector( AVSDirectory.initialize.selector, - msg.sender, - pauserRegistry, - 0 /*initialPausedStatus*/ + msg.sender, // initialOwner + 0 // initialPausedStatus ) ) ) ); rewardsCoordinatorMock = new RewardsCoordinatorMock(); - - strategyManagerMock.setAddresses(delegationMock, eigenPodManagerMock, slasher); + strategyManagerMock.setDelegationManager(delegationMock); cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); @@ -191,52 +176,49 @@ contract MockAVSDeployer is Test { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - stakeRegistry = StakeRegistryHarness( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - indexRegistry = IndexRegistry( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - blsApkRegistry = BLSApkRegistryHarness( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - serviceManager = ServiceManagerMock( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - + allocationManager = AllocationManager( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); cheats.stopPrank(); cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = - new StakeRegistryHarness(IRegistryCoordinator(registryCoordinator), delegationMock); - + new StakeRegistryHarness(IRegistryCoordinator(registryCoordinator), delegationMock, avsDirectory, serviceManager); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), address(stakeRegistryImplementation) ); blsApkRegistryImplementation = new BLSApkRegistryHarness(registryCoordinator); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(blsApkRegistry))), address(blsApkRegistryImplementation) ); indexRegistryImplementation = new IndexRegistry(registryCoordinator); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(indexRegistry))), address(indexRegistryImplementation) @@ -246,17 +228,30 @@ contract MockAVSDeployer is Test { avsDirectoryMock, IRewardsCoordinator(address(rewardsCoordinatorMock)), registryCoordinator, - stakeRegistry + stakeRegistry, + allocationManager ); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(serviceManager))), address(serviceManagerImplementation) ); + allocationManagerImplementation = new AllocationManager( + delegationMock, + pauserRegistry, + permissionControllerMock, + uint32(7 days), // DEALLOCATION_DELAY + uint32(1 days) // ALLOCATION_CONFIGURATION_DELAY + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(allocationManager))), + address(allocationManagerImplementation) + ); + serviceManager.initialize({ initialOwner: registryCoordinatorOwner, - rewardsInitiator: address(proxyAdminOwner) + rewardsInitiator: proxyAdminOwner, + slasher: proxyAdminOwner }); // set the public key for an operator, using harnessed function to bypass checks @@ -279,7 +274,7 @@ contract MockAVSDeployer is Test { } registryCoordinatorImplementation = new RegistryCoordinatorHarness( - serviceManager, stakeRegistry, blsApkRegistry, indexRegistry + serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, pauserRegistry ); { delete operatorSetParams; @@ -294,21 +289,33 @@ contract MockAVSDeployer is Test { ); } + // Create arrays for quorum types and lookahead periods + StakeType[] memory quorumStakeTypes = new StakeType[](numQuorumsToAdd); + uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](numQuorumsToAdd); + + // Set all quorums to TOTAL_DELEGATED type with 0 lookahead period + for (uint256 i = 0; i < numQuorumsToAdd; i++) { + quorumStakeTypes[i] = StakeType.TOTAL_DELEGATED; + slashableStakeQuorumLookAheadPeriods[i] = 0; + } + proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), - abi.encodeWithSelector( - RegistryCoordinator.initialize.selector, - registryCoordinatorOwner, - churnApprover, - ejector, - pauserRegistry, - 0, /*initialPausedStatus*/ - operatorSetParams, - minimumStakeForQuorum, - quorumStrategiesConsideredAndMultipliers - ) - ); + abi.encodeCall( + RegistryCoordinator.initialize, + ( + registryCoordinatorOwner, // _initialOwner + churnApprover, // _churnApprover + ejector, // _ejector + 0, // _initialPausedStatus + operatorSetParams, // _operatorSetParams + minimumStakeForQuorum, // _minimumStakes + quorumStrategiesConsideredAndMultipliers, // _strategyParams + quorumStakeTypes, // _stakeTypes + slashableStakeQuorumLookAheadPeriods // _lookAheadPeriods + ) + )); } operatorStateRetriever = new OperatorStateRetriever(); @@ -316,6 +323,31 @@ contract MockAVSDeployer is Test { cheats.stopPrank(); } + function _labelContracts() internal { + vm.label(address(emptyContract), "EmptyContract"); + vm.label(address(proxyAdmin), "ProxyAdmin"); + vm.label(address(pauserRegistry), "PauserRegistry"); + vm.label(address(delegationMock), "DelegationMock"); + vm.label(address(avsDirectoryMock), "AVSDirectoryMock"); + vm.label(address(eigenPodManagerMock), "EigenPodManagerMock"); + vm.label(address(strategyManagerMock), "StrategyManagerMock"); + vm.label(address(allocationManagerMock), "AllocationManagerMock"); + vm.label(address(avsDirectoryImplementation), "AVSDirectoryImplementation"); + vm.label(address(avsDirectory), "AVSDirectory"); + vm.label(address(rewardsCoordinatorMock), "RewardsCoordinatorMock"); + vm.label(address(registryCoordinator), "RegistryCoordinator"); + vm.label(address(stakeRegistry), "StakeRegistry"); + vm.label(address(indexRegistry), "IndexRegistry"); + vm.label(address(blsApkRegistry), "BLSApkRegistry"); + vm.label(address(serviceManager), "ServiceManager"); + vm.label(address(allocationManager), "AllocationManager"); + vm.label(address(stakeRegistryImplementation), "StakeRegistryImplementation"); + vm.label(address(blsApkRegistryImplementation), "BLSApkRegistryImplementation"); + vm.label(address(indexRegistryImplementation), "IndexRegistryImplementation"); + vm.label(address(serviceManagerImplementation), "ServiceManagerImplementation"); + vm.label(address(allocationManagerImplementation), "AllocationManagerImplementation"); + } + /** * @notice registers operator with coordinator */ diff --git a/test/utils/NoInitializer.sol b/test/utils/NoInitializer.sol deleted file mode 100644 index e70616ae..00000000 --- a/test/utils/NoInitializer.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract NoInitializer { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 public immutable a; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(uint256 _a) { - a = _a; - } -} - diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 95ab65e9..266ed8d8 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; @@ -50,12 +50,12 @@ contract Operators is Test { X: [ readUint(operatorConfigJson, index, "PubkeyG2.X.A1"), readUint(operatorConfigJson, index, "PubkeyG2.X.A0") - ], + ], Y: [ readUint(operatorConfigJson, index, "PubkeyG2.Y.A1"), readUint(operatorConfigJson, index, "PubkeyG2.Y.A0") ] - }); + }); return pubkey; } @@ -68,7 +68,7 @@ contract Operators is Test { uint256 result = 0; for (uint256 i = 0; i < b.length; i++) { if (uint256(uint8(b[i])) >= 48 && uint256(uint8(b[i])) <= 57) { - result = result * 10 + (uint256(uint8(b[i])) - 48); + result = result * 10 + (uint256(uint8(b[i])) - 48); } } return result; diff --git a/test/utils/Owners.sol b/test/utils/Owners.sol index edb577d4..cdb3add2 100644 --- a/test/utils/Owners.sol +++ b/test/utils/Owners.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "forge-std/Script.sol"; @@ -29,7 +29,7 @@ contract Owners is Test { for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } function getReputedOwnerAddresses() public returns(address[] memory) { @@ -37,11 +37,11 @@ contract Owners is Test { for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } function resetOwnersConfigJson(string memory newConfig) public { ownersConfigJson = vm.readFile(newConfig); } - + } diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index f331c2a1..14ad89c6 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; @@ -83,7 +83,7 @@ contract ProofParsing is Test{ function getExecutionPayloadProof () public returns(bytes32[7] memory) { for (uint i = 0; i < 7; i++) { prefix = string.concat(".ExecutionPayloadProof[", string.concat(vm.toString(i), "]")); - executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return executionPayloadProof; } @@ -91,7 +91,7 @@ contract ProofParsing is Test{ function getTimestampProof() public returns(bytes32[4] memory) { for (uint i = 0; i < 4; i++) { prefix = string.concat(".TimestampProof[", string.concat(vm.toString(i), "]")); - timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return timestampProofs; } @@ -99,7 +99,7 @@ contract ProofParsing is Test{ function getBlockHeaderProof() public returns(bytes32[18] memory) { for (uint i = 0; i < 18; i++) { prefix = string.concat(".BlockHeaderProof[", string.concat(vm.toString(i), "]")); - blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return blockHeaderProof; } @@ -107,7 +107,7 @@ contract ProofParsing is Test{ function getSlotProof() public returns(bytes32[3] memory) { for (uint i = 0; i < 3; i++) { prefix = string.concat(".SlotProof[", string.concat(vm.toString(i), "]")); - slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return slotProof; } @@ -116,7 +116,7 @@ contract ProofParsing is Test{ bytes32[] memory stateRootProof = new bytes32[](3); for (uint i = 0; i < 3; i++) { prefix = string.concat(".StateRootAgainstLatestBlockHeaderProof[", string.concat(vm.toString(i), "]")); - stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return stateRootProof; } @@ -124,7 +124,7 @@ contract ProofParsing is Test{ function getWithdrawalProof() public returns(bytes32[9] memory) { for (uint i = 0; i < 9; i++) { prefix = string.concat(".WithdrawalProof[", string.concat(vm.toString(i), "]")); - withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalProof; } @@ -132,7 +132,7 @@ contract ProofParsing is Test{ function getValidatorProof() public returns(bytes32[46] memory) { for (uint i = 0; i < 46; i++) { prefix = string.concat(".ValidatorProof[", string.concat(vm.toString(i), "]")); - validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorProof; } @@ -140,16 +140,16 @@ contract ProofParsing is Test{ function getHistoricalSummaryProof() public returns(bytes32[44] memory) { for (uint i = 0; i < 44; i++) { prefix = string.concat(".HistoricalSummaryProof[", string.concat(vm.toString(i), "]")); - historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return historicalSummaryProof; } - + function getWithdrawalFields() public returns(bytes32[] memory) { bytes32[] memory withdrawalFields = new bytes32[](4); for (uint i = 0; i < 4; i++) { prefix = string.concat(".WithdrawalFields[", string.concat(vm.toString(i), "]")); - withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalFields; @@ -159,7 +159,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorFields = new bytes32[](8); for (uint i = 0; i < 8; i++) { prefix = string.concat(".ValidatorFields[", string.concat(vm.toString(i), "]")); - validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFields; } @@ -168,7 +168,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorBalanceProof = new bytes32[](44); for (uint i = 0; i < 44; i++) { prefix = string.concat(".ValidatorBalanceProof[", string.concat(vm.toString(i), "]")); - validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorBalanceProof; } @@ -177,7 +177,7 @@ contract ProofParsing is Test{ bytes32[] memory balanceUpdateSlotProof = new bytes32[](5); for (uint i = 0; i < 5; i++) { prefix = string.concat(".slotProof[", string.concat(vm.toString(i), "]")); - balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return balanceUpdateSlotProof; } @@ -186,7 +186,7 @@ contract ProofParsing is Test{ bytes32[] memory withdrawalCredenitalProof = new bytes32[](46); for (uint i = 0; i < 46; i++) { prefix = string.concat(".WithdrawalCredentialProof[", string.concat(vm.toString(i), "]")); - withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalCredenitalProof; } @@ -195,7 +195,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorFieldsProof = new bytes32[](46); for (uint i = 0; i < 46; i++) { prefix = string.concat(".ValidatorFieldsProof[", string.concat(vm.toString(i), "]")); - validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFieldsProof; } diff --git a/test/utils/ProxyTestContracts.sol b/test/utils/ProxyTestContracts.sol deleted file mode 100644 index af2bc907..00000000 --- a/test/utils/ProxyTestContracts.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Greeter} from "./Greeter.sol"; -import {GreeterProxiable} from "./GreeterProxiable.sol"; -import {GreeterV2} from "./GreeterV2.sol"; -import {GreeterV2Proxiable} from "./GreeterV2Proxiable.sol"; -import {WithConstructor} from "./WithConstructor.sol"; -import {NoInitializer} from "./NoInitializer.sol"; diff --git a/test/utils/SignatureCompaction.sol b/test/utils/SignatureCompaction.sol index c10b2db1..0bb98a1c 100644 --- a/test/utils/SignatureCompaction.sol +++ b/test/utils/SignatureCompaction.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/test/utils/UpgradeableProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol deleted file mode 100644 index 6ce5471a..00000000 --- a/test/utils/UpgradeableProxyUtils.sol +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Vm} from "forge-std/Vm.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -/// Modified from the Openzeppelin foundry upgrades library -/// Modifications: -/// - Made compatible with OZ ^4.x releases -/// - Removed OZ Defender functionality -library UpgradeableProxyUtils { - address private constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - - // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 - bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; - - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. - * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. - */ - bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - - /** - * @dev Storage slot with the admin of the contract. - * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - - Vm private constant vm = Vm(CHEATCODE_ADDRESS); - - /** - * @dev Deploys a transparent proxy using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy - * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployTransparentProxy( - string memory contractName, - address initialOwner, - bytes memory initializerData - ) internal returns (address) { - return deployTransparentProxy(contractName, initialOwner, initializerData, ""); - } - - /** - * @dev Deploys a transparent proxy using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy - * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployTransparentProxy( - string memory contractName, - address initialOwner, - bytes memory initializerData, - bytes memory implConstructorArgs - ) internal returns (address) { - address impl = deployImplementation(contractName, implConstructorArgs); - return - address( - _deploy( - "TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy", - abi.encode(impl, initialOwner, initializerData) - ) - ); - } - - /** - * @dev Deploys an upgradeable beacon using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the UpgradeableBeacon contract which gets deployed - * @return Beacon address - */ - function deployBeacon( - string memory contractName, - address initialOwner, - bytes memory implConstructorArgs - ) internal returns (address) { - address impl = deployImplementation(contractName, implConstructorArgs); - return _deploy("UpgradeableBeacon.sol:UpgradeableBeacon", abi.encode(impl, initialOwner)); - } - - /** - * @dev Deploys a beacon proxy using the given beacon and call data. - * - * @param beacon Address of the beacon to use - * @param data Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployBeaconProxy(address beacon, bytes memory data) internal returns (address) { - return _deploy("BeaconProxy.sol:BeaconProxy", abi.encode(beacon, data)); - } - - /** - * @dev Validates and deploys an implementation contract, and returns its address. - * - * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @return Address of the implementation contract - */ - function deployImplementation(string memory contractName, bytes memory implConstructorArgs) internal returns (address) { - return _deploy(contractName, implConstructorArgs); - } - /** - * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. - * @param proxy Address of a transparent proxy - * @return Admin address - */ - function getAdminAddress(address proxy) internal view returns (address) { - bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); - return address(uint160(uint256(adminSlot))); - } - - /** - * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. - * @param proxy Address of a transparent or UUPS proxy - * @return Implementation address - */ - function getImplementationAddress(address proxy) internal view returns (address) { - bytes32 implSlot = vm.load(proxy, _IMPLEMENTATION_SLOT); - return address(uint160(uint256(implSlot))); - } - - /** - * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. - * @param proxy Address of a beacon proxy - * @return Beacon address - */ - function getBeaconAddress(address proxy) internal view returns (address) { - bytes32 beaconSlot = vm.load(proxy, _BEACON_SLOT); - return address(uint160(uint256(beaconSlot))); - } - - /** - * @dev Upgrades a proxy to a new implementation contract. - * @param proxy Address of the proxy to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract - */ - function upgradeProxy( - address proxy, - string memory contractName, - bytes memory data, - bytes memory implConstructorArgs - ) internal { - address newImpl = _deploy(contractName, implConstructorArgs); - - bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); - if (adminSlot == bytes32(0)) { - // No admin contract: upgrade directly using interface - TransparentUpgradeableProxy(payable(proxy)).upgradeToAndCall(newImpl, data); - } else { - ProxyAdmin admin = ProxyAdmin(address(uint160(uint256(adminSlot)))); - admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), newImpl, data); - } - } - - /** - * @dev Upgrades a proxy to a new implementation contract. - * @param proxy Address of the proxy to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - */ - function upgradeProxy(address proxy, string memory contractName, bytes memory data) internal { - upgradeProxy(proxy, contractName, data, ""); - } - - /** - * @dev Upgrades a beacon to a new implementation contract. - * @param beacon Address of the beacon to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract - */ - function upgradeBeacon(address beacon, string memory contractName, bytes memory implConstructorArgs) internal { - address newImpl = _deploy(contractName, implConstructorArgs); - UpgradeableBeacon(beacon).upgradeTo(newImpl); - } - - /* - * @param beacon Address of the beacon to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - */ - function upgradeBeacon(address beacon, string memory contractName) internal { - upgradeBeacon(beacon, contractName, ""); - } - - function _deploy(string memory contractName, bytes memory implConstructorArgs) private returns (address) { - bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); - address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, implConstructorArgs)); - if (deployedAddress == address(0)) { - revert( - string.concat( - "Failed to deploy contract ", - contractName, - ' using constructor data "', - string(implConstructorArgs), - '"' - ) - ); - } - return deployedAddress; - } - - function _deployFromBytecode(bytes memory bytecode) private returns (address) { - address addr; - assembly { - addr := create(0, add(bytecode, 32), mload(bytecode)) - } - return addr; - } - - /** - * @dev Precompile proxy contracts so that they can be deployed by name via the `_deploy` function. - * - * NOTE: This function is never called and has no effect, but must be kept to ensure that the proxy contracts are included in the compilation. - */ - function _precompileProxyContracts() private pure { - bytes memory dummy; - dummy = type(ERC1967Proxy).creationCode; - dummy = type(TransparentUpgradeableProxy).creationCode; - dummy = type(ProxyAdmin).creationCode; - dummy = type(UpgradeableBeacon).creationCode; - dummy = type(BeaconProxy).creationCode; - } -} diff --git a/test/utils/UpgradeableProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol deleted file mode 100644 index f0197078..00000000 --- a/test/utils/UpgradeableProxyUtils.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Test, console} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; - -import {UpgradeableProxyUtils} from "./UpgradeableProxyUtils.sol"; -import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; -import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; - -contract UpgradeableProxyUtilsTest is Test { - ProxyAdmin internal admin; - - function setUp() public { - admin = new ProxyAdmin(); - } - - function testTransparent() public { - address proxy = UpgradeableProxyUtils.deployTransparentProxy( - "Greeter.sol", - address(admin), - abi.encodeCall(Greeter.initialize, ("hello")) - ); - Greeter instance = Greeter(proxy); - address implAddressV1 = UpgradeableProxyUtils.getImplementationAddress(proxy); - address adminAddress = UpgradeableProxyUtils.getAdminAddress(proxy); - - assertFalse(adminAddress == address(0)); - assertEq(instance.greeting(), "hello"); - - UpgradeableProxyUtils.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ())); - - address implAddressV2 = UpgradeableProxyUtils.getImplementationAddress(proxy); - - assertEq(UpgradeableProxyUtils.getAdminAddress(proxy), adminAddress); - assertEq(instance.greeting(), "resetted"); - assertFalse(implAddressV2 == implAddressV1); - } - - function testBeacon() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); - address implAddressV1 = IBeacon(beacon).implementation(); - - address proxy = UpgradeableProxyUtils.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); - Greeter instance = Greeter(proxy); - - assertEq(UpgradeableProxyUtils.getBeaconAddress(proxy), beacon); - assertEq(instance.greeting(), "hello"); - - UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol"); - address implAddressV2 = IBeacon(beacon).implementation(); - - GreeterV2(address(instance)).resetGreeting(); - - assertEq(instance.greeting(), "resetted"); - assertFalse(implAddressV2 == implAddressV1); - } - - function testUpgradeBeaconWithoutCaller() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); - UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol", abi.encode()); - } - - function testWithConstructor() public { - bytes memory constructorData = abi.encode(123); - address proxy = UpgradeableProxyUtils.deployTransparentProxy( - "WithConstructor.sol", - msg.sender, - abi.encodeCall(WithConstructor.initialize, (456)), - constructorData - ); - - assertEq(WithConstructor(proxy).a(), 123); - assertEq(WithConstructor(proxy).b(), 456); - } - - function testNoInitializer() public { - /// Can access getCode by File:Contract - bytes memory constructorData = abi.encode(123); - address proxy = UpgradeableProxyUtils.deployTransparentProxy("NoInitializer.sol", msg.sender, "", constructorData); - - assertEq(WithConstructor(proxy).a(), 123); - } - -} diff --git a/test/utils/WithConstructor.sol b/test/utils/WithConstructor.sol deleted file mode 100644 index 146b3ecd..00000000 --- a/test/utils/WithConstructor.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract WithConstructor { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 public immutable a; - - uint256 public b; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(uint256 _a) { - a = _a; - } - - function initialize(uint256 _b) public { - b = _b; - } -} -