├── .devcontainer ├── Dockerfile ├── devcontainer.json └── install.sh ├── .env.example ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── design.md │ ├── feature_request.md │ ├── implement.md │ ├── research.md │ ├── task.md │ └── test_implementation.md └── workflows │ ├── certora-prover.yml │ ├── commitlint.yml │ ├── run-deploy-scripts.yml │ ├── slither.yml │ └── testinparallel.yml ├── .gitignore ├── .gitmodules ├── .husky └── commit-msg ├── .prettierrc ├── .solhint.json ├── .solhintignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── audits ├── M1 Mainnet - Diligence - Mar 2023.pdf ├── M1 Mainnet - Sigma Prime - May 2023.pdf ├── M2 Mainnet - Cantina - Apr 2024.pdf └── M2 Mainnet - Sigma Prime - Feb 2024.pdf ├── certora ├── harnesses │ ├── DelegationManagerHarness.sol │ ├── EigenPodHarness.sol │ ├── EigenPodManagerHarness.sol │ ├── PausableHarness.sol │ ├── SlasherHarness.sol │ ├── StrategyManagerHarness.sol │ └── StructuredLinkedListHarness.sol ├── scripts │ ├── core │ │ ├── verifyDelegationManager.sh │ │ └── verifyStrategyManager.sh │ ├── libraries │ │ └── verifyStructuredLinkedList.sh │ ├── permissions │ │ └── verifyPausable.sh │ ├── pods │ │ ├── verifyEigenPod.sh │ │ └── verifyEigenPodManager.sh │ └── strategies │ │ └── verifyStrategyBase.sh └── specs │ ├── core │ ├── DelegationManager.spec │ ├── Slasher.spec │ └── StrategyManager.spec │ ├── libraries │ └── StructuredLinkedList.spec │ ├── permissions │ └── Pausable.spec │ ├── pods │ ├── EigenPod.spec │ └── EigenPodManager.spec │ └── strategies │ └── StrategyBase.spec ├── commitlint.config.js ├── config.yml ├── docs ├── README.md ├── core │ ├── AVSDirectory.md │ ├── DelegationManager.md │ ├── EigenPodManager.md │ ├── StrategyManager.md │ └── proofs │ │ └── BeaconChainProofs.md ├── experimental │ └── AVS-Guide.md └── images │ ├── EL_completing_queued_withdrawal.png │ ├── EL_delegating.png │ ├── EL_depositing.png │ ├── EL_depositing_BeaconChainETH.png │ ├── EL_depositing_BeaconChainETH_2.png │ ├── EL_eigenpods_architecture.png │ ├── EL_operator_registration.png │ ├── EL_queuing_a_withdrawal.png │ ├── EigenPods_Architecture.png │ ├── Staker Flow Diagrams │ ├── Complete Withdrawal as Shares.png │ ├── Complete Withdrawal as Tokens.png │ ├── Delegating.png │ ├── Depositing.png │ ├── Partial Withdrawals.png │ ├── Queue Withdrawal.png │ └── Validator Exits.png │ ├── Withdrawal_Credential_Proof.png │ ├── Withdrawal_Proof.png │ ├── Withdrawal_Proof_Diagram.png │ ├── middleware_outline_doc.png │ ├── operator_deregister.png │ ├── operator_opting.png │ ├── samplemerkle.png │ ├── slashing.png │ ├── staker_opting.png │ ├── staker_withdrawing.png │ ├── staterootproof.png │ ├── three_middlewares.png │ ├── three_middlewares_withdrawal_queued.png │ └── withdrawal.png ├── foundry.toml ├── hardhat.config.ts ├── mythril.config.json ├── package-lock.json ├── package.json ├── remappings.txt ├── requirements.txt ├── script ├── admin │ └── mainnet │ │ └── Mainnet_Unpause_Deposits.s.sol ├── configs │ ├── devnet │ │ ├── M1_deploy_devnet.config.json │ │ └── M2_deploy_from_scratch.anvil.config.json │ ├── goerli │ │ └── M1_deploy_goerli.config.json │ ├── holesky │ │ ├── Holesky_current_deployment.config.json │ │ ├── M2_deploy_from_scratch.holesky.config.json │ │ └── M2_deploy_preprod.holesky.config.json │ └── mainnet │ │ ├── M1_deploy_mainnet.config.json │ │ ├── M2_deploy_from_scratch.mainnet.config.json │ │ ├── M2_mainnet_upgrade.config.json │ │ ├── Mainnet_current_deployment.config.json │ │ └── Mainnet_current_eigenPods.config.json ├── deploy │ ├── devnet │ │ └── M2_Deploy_From_Scratch.s.sol │ ├── goerli │ │ ├── GoerliUpgrade1.s.sol │ │ └── GoerliUpgrade2.s.sol │ ├── holesky │ │ ├── M2_Deploy_From_Scratch.s.sol │ │ └── M2_Deploy_Preprod.s.sol │ └── mainnet │ │ ├── M1_Deploy.s.sol │ │ ├── M2Deploy.s.sol │ │ └── M2_Mainnet_Upgrade.s.sol ├── output │ ├── devnet │ │ ├── M1_MOCK_deployment_data.json │ │ └── M2_from_scratch_deployment_data.json │ ├── goerli │ │ ├── GV2_deployment_2024_6_2.json │ │ ├── GV2_preprod_deployment_2024_30_1.json │ │ ├── M1_deployment_goerli_2023_3_23.json │ │ ├── M2_deployment_data_goerli.json │ │ ├── M2_preprod_deployment_from_scratch.json │ │ └── deployment_output.json │ └── mainnet │ │ ├── M1_deployment_mainnet_2023_6_9.json │ │ └── M2_mainnet_upgrade.output.json ├── utils │ ├── ExistingDeploymentParser.sol │ ├── Multisend.sol │ ├── TimelockEncoding.sol │ ├── TxEncodingInterfaces.sol │ └── validateStorage │ │ ├── README.md │ │ ├── validateStorage.ts │ │ └── validateUpgrade.sh └── whitelist │ ├── ERC20PresetMinterPauser.sol │ ├── Staker.sol │ └── delegationFaucet │ ├── DelegationFaucet.sol │ ├── DelegationFaucetStaker.sol │ └── DeployDelegationFaucet.sol ├── slither.config.json ├── source-env.sh ├── src ├── contracts │ ├── core │ │ ├── AVSDirectory.sol │ │ ├── AVSDirectoryStorage.sol │ │ ├── DelegationManager.sol │ │ ├── DelegationManagerStorage.sol │ │ ├── Slasher.sol │ │ ├── StrategyManager.sol │ │ └── StrategyManagerStorage.sol │ ├── interfaces │ │ ├── IAVSDirectory.sol │ │ ├── IBeaconChainOracle.sol │ │ ├── IDelayedWithdrawalRouter.sol │ │ ├── IDelegationFaucet.sol │ │ ├── IDelegationManager.sol │ │ ├── IETHPOSDeposit.sol │ │ ├── IEigenPod.sol │ │ ├── IEigenPodManager.sol │ │ ├── IPausable.sol │ │ ├── IPauserRegistry.sol │ │ ├── ISignatureUtils.sol │ │ ├── ISlasher.sol │ │ ├── ISocketUpdater.sol │ │ ├── IStrategy.sol │ │ ├── IStrategyManager.sol │ │ └── IWhitelister.sol │ ├── libraries │ │ ├── BeaconChainProofs.sol │ │ ├── BytesLib.sol │ │ ├── EIP1271SignatureUtils.sol │ │ ├── Endian.sol │ │ ├── Merkle.sol │ │ └── StructuredLinkedList.sol │ ├── permissions │ │ ├── Pausable.sol │ │ └── PauserRegistry.sol │ ├── pods │ │ ├── DelayedWithdrawalRouter.sol │ │ ├── EigenPod.sol │ │ ├── EigenPodManager.sol │ │ ├── EigenPodManagerStorage.sol │ │ └── EigenPodPausingConstants.sol │ ├── strategies │ │ ├── StrategyBase.sol │ │ └── StrategyBaseTVLLimits.sol │ └── utils │ │ └── UpgradeableSignatureCheckingUtils.sol └── test │ ├── Delegation.t.sol │ ├── DelegationFaucet.t.sol │ ├── DepositWithdraw.t.sol │ ├── EigenLayerDeployer.t.sol │ ├── EigenLayerTestHelper.t.sol │ ├── EigenPod.t.sol │ ├── Pausable.t.sol │ ├── Strategy.t.sol │ ├── WithdrawalMigration.t.sol │ ├── Withdrawals.t.sol │ ├── events │ ├── IAVSDirectoryEvents.sol │ ├── IDelegationManagerEvents.sol │ ├── IEigenPodEvents.sol │ ├── IEigenPodManagerEvents.sol │ └── IStrategyManagerEvents.sol │ ├── harnesses │ ├── EigenPodHarness.sol │ ├── EigenPodManagerWrapper.sol │ └── PausableHarness.sol │ ├── integration │ ├── IntegrationBase.t.sol │ ├── IntegrationChecks.t.sol │ ├── IntegrationDeployer.t.sol │ ├── README.md │ ├── TimeMachine.t.sol │ ├── deprecatedInterfaces │ │ └── mainnet │ │ │ ├── BeaconChainProofs.sol │ │ │ ├── IBeaconChainOracle.sol │ │ │ ├── IDelayedWithdrawalRouter.sol │ │ │ ├── IEigenPod.sol │ │ │ ├── IEigenPodManager.sol │ │ │ └── IStrategyManager.sol │ ├── mocks │ │ ├── BeaconChainMock.t.sol │ │ └── BeaconChainOracleMock.t.sol │ ├── tests │ │ ├── Delegate_Deposit_Queue_Complete.t.sol │ │ ├── Deposit_Delegate_Queue_Complete.t.sol │ │ ├── Deposit_Delegate_Redelegate_Complete.t.sol │ │ ├── Deposit_Delegate_Undelegate_Complete.t.sol │ │ ├── Deposit_Delegate_UpdateBalance.t.sol │ │ ├── Deposit_Queue_Complete.t.sol │ │ ├── Deposit_Register_QueueWithdrawal_Complete.t.sol │ │ └── Upgrade_Setup.t.sol │ └── users │ │ ├── User.t.sol │ │ └── User_M1.t.sol │ ├── mocks │ ├── BeaconChainOracleMock.sol │ ├── DelayedWithdrawalRouterMock.sol │ ├── DelegationManagerMock.sol │ ├── Dummy.sol │ ├── ERC20Mock.sol │ ├── ERC20_OneWeiFeeOnTransfer.sol │ ├── ERC20_SetTransferReverting_Mock.sol │ ├── ETHDepositMock.sol │ ├── EigenPodManagerMock.sol │ ├── EigenPodMock.sol │ ├── EmptyContract.sol │ ├── IBeaconChainOracleMock.sol │ ├── LiquidStakingToken.sol │ ├── OwnableMock.sol │ ├── Reenterer.sol │ ├── Reverter.sol │ ├── SlasherMock.sol │ └── StrategyManagerMock.sol │ ├── test-data │ ├── balanceUpdateProof_balance28ETH_302913.json │ ├── balanceUpdateProof_notOverCommitted_302913.json │ ├── balanceUpdateProof_notOverCommitted_302913_incrementedBlockBy100.json │ ├── balanceUpdateProof_updated_to_0ETH_302913.json │ ├── balanceUpdateProof_updated_to_30ETH_302913.json │ ├── fullWithdrawalCapellaAgainstDenebRoot.json │ ├── fullWithdrawalDeneb.json │ ├── fullWithdrawalProof_Latest.json │ ├── fullWithdrawalProof_Latest_1SlotAdvanced.json │ ├── fullWithdrawalProof_Latest_28ETH.json │ ├── operators.json │ ├── owners.json │ ├── partialWithdrawalProof_Latest.json │ ├── reputedOwners.json │ ├── slashedProofs │ │ ├── balanceUpdateProof_Overcommitted_61511.json │ │ ├── balanceUpdateProof_notOvercommitted_61511.json │ │ └── balanceUpdateProof_notOvercommitted_61511_incrementedBlockBy100.json │ ├── withdrawalCredentialAndBalanceProof_61068.json │ ├── withdrawalCredentialAndBalanceProof_61336.json │ ├── withdrawal_credential_proof_302913.json │ ├── withdrawal_credential_proof_302913_30ETHBalance.json │ ├── withdrawal_credential_proof_302913_exited.json │ └── withdrawal_credential_proof_510257.json │ ├── tree │ ├── DelegationManagerUnit.tree │ ├── EigenPodManagerUnit.tree │ ├── EigenPodUnit.tree │ └── StrategyManagerUnit.tree │ ├── unit │ ├── AVSDirectoryUnit.t.sol │ ├── DelayedWithdrawalRouterUnit.t.sol │ ├── DelegationUnit.t.sol │ ├── EigenPod-PodManagerUnit.t.sol │ ├── EigenPodManagerUnit.t.sol │ ├── EigenPodUnit.t.sol │ ├── PausableUnit.t.sol │ ├── PauserRegistryUnit.t.sol │ ├── StrategyBaseTVLLimitsUnit.sol │ ├── StrategyBaseUnit.t.sol │ └── StrategyManagerUnit.t.sol │ └── utils │ ├── EigenLayerUnitTestBase.sol │ ├── EigenLayerUnitTestSetup.sol │ ├── Operators.sol │ ├── Owners.sol │ ├── ProofParsing.sol │ ├── SignatureCompaction.sol │ └── Utils.sol ├── test.sh ├── testHelpers ├── cleProofGenerator.js ├── package.json └── sign.js └── tsconfig.json /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Ubuntu version (use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon): ubuntu-22.04, ubuntu-20.04, ubuntu-18.04 2 | ARG VARIANT=ubuntu-22.04 3 | FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} 4 | 5 | # [Optional] Uncomment this section to install additional OS packages. 6 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 7 | && apt-get -y install --no-install-recommends libssl-dev -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/go 3 | { 4 | "name": "Ubuntu", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | }, 8 | 9 | // Configure tool-specific properties. 10 | "customizations": { 11 | // Configure access control to other repositories 12 | "codespaces": { 13 | "repositories": { 14 | "Layr-Labs/*": { 15 | "permissions": "write-all" 16 | } 17 | } 18 | }, 19 | // Configure properties specific to VS Code. 20 | "vscode": { 21 | // Add the IDs of extensions you want installed when the container is created. 22 | "extensions": [ 23 | "NomicFoundation.hardhat-solidity", 24 | "GitHub.copilot" 25 | ] 26 | } 27 | }, 28 | 29 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 30 | // "forwardPorts": [], 31 | 32 | // Use 'postCreateCommand' to run commands after the container is created. 33 | "postCreateCommand": "chmod +x ./.devcontainer/install.sh && bash ./.devcontainer/install.sh", 34 | 35 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 36 | "remoteUser": "vscode" 37 | } 38 | -------------------------------------------------------------------------------- /.devcontainer/install.sh: -------------------------------------------------------------------------------- 1 | # Install foundry 2 | curl -L https://foundry.paradigm.xyz | bash 3 | ~/.foundry/bin/foundryup -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | RPC_MAINNET="https://eth.llamarpc.com" 2 | # RPC_MAINNET="https://mainnet.infura.io/v3/API-KEY" 3 | RPC_GOERLI="https://ethereum-goerli.publicnode.com" 4 | RPC_HOLESKY="" 5 | ETHERSCAN_API_KEY="API-KEY" 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: Create a report to help us improve 4 | title: 'Bug: Title' 5 | labels: bug 6 | 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Environment** 26 | Enter important environment info needed to reproduce the bug. 27 | - [e.g. chrome, safari] 28 | - [e.g. version] 29 | 30 | **Don't Forget To** 31 | * Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) 32 | * Add priority + size estimate 33 | * Set status to New 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/design.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Design 3 | about: Design 4 | title: 'Design: Title' 5 | labels: design 6 | 7 | --- 8 | 9 | ## Description 10 | Add a summary and description. What are we trying to do and why? 11 | 12 | ### Action Items 13 | - [ ] Come up with scheme for... 14 | - [ ] Spec it in a HackMD or Google Doc that is linked here 15 | - [ ] Get @ to review design 16 | - [ ] Discuss design and tradeoffs with @ 17 | - [ ] Finalize the doc, and come to consensus 18 | - [ ] Create ticket for implementation and link the doc to it 19 | 20 | ### Blocking Issues 21 | Is anything blocking for this? (Does this depend on other unfinished designs or pending changes?) 22 | Please link blocking issues here. If something is blocking and doesn't have an issue yet, create it! 23 | 24 | ### Don't Forget To 25 | * Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) 26 | * Add priority + size estimate 27 | * Set status to New 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature 3 | about: Suggest an idea for this project 4 | title: 'Feature: Title' 5 | labels: feature 6 | 7 | --- 8 | 9 | **User story** 10 | User stories are often expressed in a simple sentence, structured as follows: 'As a PERSONA, I want to GOAL_DESCRIPTION, so that CUSTOMER_VALUE_DESCRIPTION.' Alternatively for 11 | 12 | **Actions** 13 | - [ ] An action item list describing the work to be done 14 | - [ ] Link to all of the related tasks needed to complete the feature. See the [tasks](https://github.com/Layr-Labs/eigenlayer-contracts/tree/master/.github/ISSUE_TEMPLATE/task.md) template. 15 | - [ ] Include everything in the definition of done e.g. unit tests and documentation 16 | 17 | **Acceptance criteria** 18 | Acceptance criteria are the requirements that need to be met in order to mark a user story as complete. For example, if your user story is, "As a New Relic customer, I want to know how to interpret AI anomalies in order to monitor my site and protect myself against incidents," then the acceptance criteria would be: "A complete and published doc describing AI anomalies," and all related quality checks. 19 | 20 | **Don't Forget To** 21 | * Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) 22 | * Add priority + size estimate 23 | * Set status to New 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/implement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Implementation 3 | about: Implementation 4 | title: 'Implement: Title' 5 | labels: implement 6 | 7 | --- 8 | 9 | ## Description 10 | If we already have a design doc then link it [here](). 11 | Otherwise at least add a summary and description. 12 | 13 | ### Action Items 14 | - [ ] Implement changes to the contracts on a separate branch 15 | - [ ] Update tests and scripts as necessary 16 | - [ ] Update documentation to reflect changes 17 | - [ ] Get PR reviewed 18 | - [ ] Respond to review / make any further changes 19 | - [ ] Create ticket for tests 20 | - [ ] Merge PR 21 | 22 | ### Blocking Issues 23 | Is anything blocking for this? (Do we need to design and/or build something first?) 24 | Please link blocking issues here. If something is blocking and doesn't have an issue yet, create it! 25 | 26 | ### Don't Forget To 27 | * Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) 28 | * Add priority + size estimate 29 | * Set status to New 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/research.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Research 3 | about: Research 4 | title: 'Research: Title' 5 | labels: research 6 | 7 | --- 8 | 9 | ## Description 10 | Add a summary and description. What do we need any why? Any preliminary notion of options + what we want out of them? 11 | 12 | ### Action Items 13 | - [ ] Create a list of options to evaluate 14 | - [ ] Do research, summarize findings into a doc or here 15 | - [ ] Present research, discuss next steps 16 | - [ ] Write up next steps + create new issue(s) for them 17 | 18 | ### Blocking Issues 19 | Is anything blocking for this? (Should we make another decision or scope something first?) 20 | Please link blocking issues here. If something is blocking and doesn't have an issue yet, create it! 21 | 22 | ### Don't Forget To 23 | * Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) 24 | * Add priority + size estimate 25 | * Set status to New 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Task 3 | about: Task 4 | title: 'Task: Title' 5 | labels: task 6 | 7 | --- 8 | 9 | ## Description 10 | Add a summary and description. Link to any parent [feature requests](https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/.github/ISSUE_TEMPLATE/feature_request.md) or [bug reports](https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/.github/ISSUE_TEMPLATE/bug_report.md). 11 | 12 | ### Action Items 13 | - [ ] An action item list describing the work to be done 14 | - [ ] Include everything in the definition of done e.g. unit tests and documentation 15 | 16 | ### Blocking Issues 17 | Is anything else clearly blocking for this? (Do we need to fix something else first?) 18 | Please link blocking issues here. If something is blocking and doesn't have an issue yet, create it! 19 | 20 | ### Don't Forget To 21 | * Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) 22 | * Add priority + size estimate 23 | * Set status to New 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/test_implementation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | about: Test 4 | title: 'Test: Title' 5 | labels: test 6 | 7 | --- 8 | 9 | ## Description 10 | Add a summary and description. What are we trying to do and why? 11 | 12 | ### Action Items 13 | - [ ] Discuss scoping for tests with the team 14 | - [ ] List possible test cases in this issue 15 | - [ ] Get @ to review design 16 | - [ ] Implement tests 17 | 18 | ### Blocking Issues 19 | Is anything blocking for this? (Does this depend on other unfinished designs or pending changes?) 20 | Please link blocking issues here. If something is blocking and doesn't have an issue yet, create it! 21 | 22 | ### Don't Forget To 23 | * Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) 24 | * Add priority + size estimate 25 | * Set status to New -------------------------------------------------------------------------------- /.github/workflows/certora-prover.yml: -------------------------------------------------------------------------------- 1 | name: Certora Prover 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - release-v* 8 | - formal-verification 9 | - m2-mainnet 10 | pull_request: {} 11 | workflow_dispatch: {} 12 | 13 | jobs: 14 | list-scripts: 15 | runs-on: ubuntu-latest 16 | outputs: 17 | matrix: ${{ steps.set-matrix.outputs.matrix }} 18 | steps: 19 | - uses: actions/checkout@v2 20 | - id: set-matrix 21 | run: echo ::set-output name=matrix::$(ls certora/scripts/{,**}/*.sh | grep -v '\WnoCI\W' | jq -Rsc 'split("\n")[:-1]') 22 | verify: 23 | runs-on: ubuntu-latest 24 | needs: list-scripts 25 | steps: 26 | - uses: actions/checkout@v3 27 | with: 28 | submodules: recursive 29 | - name: Install Foundry 30 | uses: foundry-rs/foundry-toolchain@v1 31 | with: 32 | version: nightly 33 | - name: Install forge dependencies 34 | run: forge install 35 | - name: Install python 36 | uses: actions/setup-python@v2 37 | with: 38 | python-version: '3.10' 39 | cache: 'pip' 40 | - name: Install java 41 | uses: actions/setup-java@v2 42 | with: 43 | distribution: temurin 44 | java-version: '17' 45 | - name: Install certora 46 | run: pip install certora-cli 47 | - name: Install solc 48 | run: | 49 | pip install solc-select 50 | solc-select use 0.8.12 --always-install 51 | - name: Verify rule ${{ matrix.params }} 52 | run: | 53 | bash ${{ matrix.params }} 54 | env: 55 | CERTORAKEY: ${{ secrets.CERTORAKEY }} 56 | 57 | strategy: 58 | fail-fast: false 59 | max-parallel: 4 60 | matrix: 61 | params: ${{ fromJson(needs.list-scripts.outputs.matrix) }} 62 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | commitlint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | with: 11 | fetch-depth: 0 12 | - name: Install node dependencies 13 | run: | 14 | npm install conventional-changelog-conventionalcommits 15 | npm install commitlint@18.2.0 16 | 17 | - name: Validate current commit (last commit) with commitlint 18 | if: github.event_name == 'push' 19 | run: npx commitlint --from HEAD~1 --to HEAD --verbose 20 | 21 | - name: Validate PR commits with commitlint 22 | if: github.event_name == 'pull_request' 23 | run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose 24 | -------------------------------------------------------------------------------- /.github/workflows/run-deploy-scripts.yml: -------------------------------------------------------------------------------- 1 | name: Run Deploy Scripts 2 | # We run the deploy scripts just to make sure they work 3 | 4 | on: 5 | push: 6 | pull_request: 7 | types: [opened, reopened] 8 | 9 | jobs: 10 | prepare: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | with: 17 | submodules: true 18 | 19 | # install foundry to run forge script. Should we run forge script in a container instead? 20 | - name: Install Foundry 21 | uses: foundry-rs/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Start Anvil chain 26 | # need to start Anvil chain with -d to let the container run in the background 27 | # if we start with 'anvil &' instead, the process stops when the step ends 28 | run: docker run -d --rm -p 8545:8545 --entrypoint anvil ghcr.io/foundry-rs/foundry:nightly-5b7e4cb3c882b28f3c32ba580de27ce7381f415a --host 0.0.0.0 29 | 30 | - name: Wait for Anvil chain to start 31 | run: sleep 3 32 | 33 | # Run Forge script against the Anvil chain 34 | - name: Run M2_Deploy_From_Scratch 35 | run: | 36 | forge script script/deploy/devnet/M2_Deploy_From_Scratch.s.sol --rpc-url http://localhost:8545 \ 37 | --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast \ 38 | --sig "run(string memory configFileName)" -- M2_deploy_from_scratch.anvil.config.json 39 | -------------------------------------------------------------------------------- /.github/workflows/slither.yml: -------------------------------------------------------------------------------- 1 | name: Slither Analysis 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: [opened, reopened] 7 | 8 | jobs: 9 | analyze: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Run Slither 15 | uses: crytic/slither-action@v0.3.0 16 | id: slither 17 | with: 18 | sarif: results.sarif 19 | fail-on: none 20 | 21 | - name: Upload SARIF file 22 | uses: github/codeql-action/upload-sarif@v2 23 | with: 24 | sarif_file: ${{ steps.slither.outputs.sarif }} -------------------------------------------------------------------------------- /.github/workflows/testinparallel.yml: -------------------------------------------------------------------------------- 1 | name: Run Parallel 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: [opened, reopened] 7 | 8 | jobs: 9 | prepare: 10 | runs-on: ubuntu-latest 11 | outputs: 12 | matrix: ${{ steps.set-matrix.outputs.matrix }} 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | 18 | - name: Get list of .t.sol files in src/test 19 | run: | 20 | FILES=$(find src/test -type f -name '*.t.sol' | sed 's#src/test/##' | jq -R -s -c 'split("\n")[:-1]') 21 | echo "::set-output name=matrix::$FILES" 22 | id: set-matrix 23 | 24 | run-tests: 25 | needs: prepare 26 | runs-on: ubuntu-latest 27 | strategy: 28 | fail-fast: false 29 | 30 | steps: 31 | - name: Checkout code 32 | uses: actions/checkout@v2 33 | 34 | - name: Install Foundry 35 | uses: foundry-rs/foundry-toolchain@v1 36 | with: 37 | version: nightly 38 | 39 | - name: Run Forge build 40 | run: | 41 | forge --version 42 | forge build --sizes 43 | id: build 44 | 45 | - name: Run unit tests 46 | run: forge test --no-match-contract Integration 47 | env: 48 | RPC_MAINNET: ${{ secrets.RPC_MAINNET }} 49 | RPC_HOLESKY: ${{ secrets.RPC_HOLESKY }} 50 | CHAIN_ID: ${{ secrets.CHAIN_ID }} 51 | 52 | - name: Run integration tests 53 | run: forge test --match-contract Integration 54 | env: 55 | RPC_MAINNET: ${{ secrets.RPC_MAINNET }} 56 | RPC_HOLESKY: ${{ secrets.RPC_HOLESKY }} 57 | CHAIN_ID: ${{ secrets.CHAIN_ID }} 58 | 59 | - name: Run integration mainnet fork tests 60 | run: forge test --match-contract Integration 61 | env: 62 | FOUNDRY_PROFILE: "forktest" 63 | RPC_MAINNET: ${{ secrets.RPC_MAINNET }} 64 | RPC_HOLESKY: ${{ secrets.RPC_HOLESKY }} 65 | CHAIN_ID: ${{ secrets.CHAIN_ID }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | *node_modules 3 | .env 4 | coverage 5 | coverage.json 6 | typechain 7 | config.json 8 | contract.addresses 9 | deployedAddresses 10 | 11 | #Hardhat files 12 | cache_hardhat 13 | artifacts 14 | *.lock 15 | #Foundry files 16 | out 17 | cache 18 | 19 | *.DS_Store 20 | 21 | broadcast 22 | 23 | # Deployment tools 24 | /data 25 | .idea/ 26 | 27 | # Certora Outputs 28 | .certora_internal/ 29 | .certora_recent_jobs.json 30 | 31 | #script config file 32 | # script/M1_deploy.config.json 33 | script/output/M1_deployment_data.json 34 | /script/output/M2_deployment_data.json 35 | 36 | # autogenerated docs (you can generate these locally) 37 | /docs/docgen/ 38 | 39 | script/misc 40 | 41 | test.sh 42 | 43 | # Surya outputs 44 | InheritanceGraph.png 45 | surya_report.md -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/openzeppelin-contracts-upgradeable"] 2 | path = lib/openzeppelin-contracts-upgradeable 3 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 4 | [submodule "lib/ds-test"] 5 | path = lib/ds-test 6 | url = https://github.com/dapphub/ds-test 7 | [submodule "lib/forge-std"] 8 | path = lib/forge-std 9 | url = https://github.com/foundry-rs/forge-std 10 | [submodule "lib/openzeppelin-contracts"] 11 | path = lib/openzeppelin-contracts 12 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 13 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit ${1} 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-solidity"], 3 | "overrides": [ 4 | { 5 | "files": "*.sol", 6 | "options": { 7 | "parser": "solidity-parse", 8 | "printWidth": 120, 9 | "tabWidth": 4, 10 | "useTabs": false, 11 | "singleQuote": false, 12 | "bracketSpacing": false 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "max-line-length": "off", 5 | "no-inline-assembly": "off", 6 | "reason-string": ["warn",{"maxLength":160}], 7 | "func-visibility": ["warn",{"ignoreConstructors":true}], 8 | "explicit-types": ["warn","explicit"], 9 | "quotes": ["warn","double"], 10 | "const-name-snakecase": "off", 11 | "not-rely-on-time": "off", 12 | "avoid-low-level-calls": "off", 13 | "contract-name-camelcase": "off", 14 | "func-name-mixedcase": "off", 15 | "var-name-mixedcase": "off", 16 | "compiler-version": "off", 17 | "custom-errors": "off", 18 | "no-global-import": "off", 19 | "immutable-vars-naming": "off" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | Slasher.sol -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | ## Requirements 3 | 4 | Foundry 5 | Git 6 | Node.js 7 | 8 | ## Setup Repo 9 | 10 | ```bash 11 | git clone git@github.com:Layr-Labs/eigenlayer-contracts.git 12 | ``` 13 | 14 | ### Install Dependencies 15 | 16 | ```bash 17 | npm install 18 | 19 | npx husky install 20 | 21 | forge install 22 | ``` 23 | 24 | ### Pull Requests 25 | 26 | All tests must pass 27 | 28 | Commits must be linted 29 | 30 | A descriptive summary of the PR has been provided. 31 | 32 | ### Environment Variables 33 | 34 | Some of the tests and features of this repository require environment variables to be set up. 35 | 36 | Copy the .env.example file to create a .env and populate it with the appropriate environment values that you have control over 37 | -------------------------------------------------------------------------------- /audits/M1 Mainnet - Diligence - Mar 2023.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/audits/M1 Mainnet - Diligence - Mar 2023.pdf -------------------------------------------------------------------------------- /audits/M1 Mainnet - Sigma Prime - May 2023.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/audits/M1 Mainnet - Sigma Prime - May 2023.pdf -------------------------------------------------------------------------------- /audits/M2 Mainnet - Cantina - Apr 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/audits/M2 Mainnet - Cantina - Apr 2024.pdf -------------------------------------------------------------------------------- /audits/M2 Mainnet - Sigma Prime - Feb 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/audits/M2 Mainnet - Sigma Prime - Feb 2024.pdf -------------------------------------------------------------------------------- /certora/harnesses/DelegationManagerHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../src/contracts/core/DelegationManager.sol"; 5 | 6 | contract DelegationManagerHarness is DelegationManager { 7 | 8 | constructor(IStrategyManager _strategyManager, ISlasher _slasher, IEigenPodManager _eigenPodManager) 9 | DelegationManager(_strategyManager, _slasher, _eigenPodManager) {} 10 | 11 | function get_operatorShares(address operator, IStrategy strategy) public view returns (uint256) { 12 | return operatorShares[operator][strategy]; 13 | } 14 | 15 | function get_stakerDelegateableShares(address staker, IStrategy strategy) public view returns (uint256) { 16 | // this is the address of the virtual 'beaconChainETH' strategy 17 | if (address(strategy) == 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0) { 18 | int256 beaconChainETHShares = eigenPodManager.podOwnerShares(staker); 19 | if (beaconChainETHShares <= 0) { 20 | return 0; 21 | } else { 22 | return uint256(beaconChainETHShares); 23 | } 24 | } else { 25 | return strategyManager.stakerStrategyShares(staker, strategy); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /certora/harnesses/EigenPodHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../src/contracts/pods/EigenPod.sol"; 5 | 6 | contract EigenPodHarness is EigenPod { 7 | 8 | constructor( 9 | IETHPOSDeposit _ethPOS, 10 | IDelayedWithdrawalRouter _delayedWithdrawalRouter, 11 | IEigenPodManager _eigenPodManager, 12 | uint64 _MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, 13 | uint64 _GENESIS_TIME 14 | ) 15 | EigenPod(_ethPOS, _delayedWithdrawalRouter, _eigenPodManager, _MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, _GENESIS_TIME) {} 16 | 17 | function get_validatorIndex(bytes32 pubkeyHash) public view returns (uint64) { 18 | return _validatorPubkeyHashToInfo[pubkeyHash].validatorIndex; 19 | } 20 | 21 | function get_restakedBalanceGwei(bytes32 pubkeyHash) public view returns (uint64) { 22 | return _validatorPubkeyHashToInfo[pubkeyHash].restakedBalanceGwei; 23 | } 24 | 25 | function get_mostRecentBalanceUpdateTimestamp(bytes32 pubkeyHash) public view returns (uint64) { 26 | return _validatorPubkeyHashToInfo[pubkeyHash].mostRecentBalanceUpdateTimestamp; 27 | } 28 | 29 | function get_podOwnerShares() public view returns (int256) { 30 | return eigenPodManager.podOwnerShares(podOwner); 31 | } 32 | 33 | function get_withdrawableRestakedExecutionLayerGwei() public view returns (uint256) { 34 | return withdrawableRestakedExecutionLayerGwei; 35 | } 36 | 37 | function get_ETH_Balance() public view returns (uint256) { 38 | return address(this).balance; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /certora/harnesses/EigenPodManagerHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../src/contracts/pods/EigenPodManager.sol"; 5 | 6 | contract EigenPodManagerHarness is EigenPodManager { 7 | 8 | constructor( 9 | IETHPOSDeposit _ethPOS, 10 | IBeacon _eigenPodBeacon, 11 | IStrategyManager _strategyManager, 12 | ISlasher _slasher, 13 | IDelegationManager _delegationManager 14 | ) 15 | EigenPodManager(_ethPOS, _eigenPodBeacon, _strategyManager, _slasher, _delegationManager) {} 16 | 17 | function get_podOwnerShares(address podOwner) public view returns (int256) { 18 | return podOwnerShares[podOwner]; 19 | } 20 | 21 | function get_podByOwner(address podOwner) public view returns (IEigenPod) { 22 | return ownerToPod[podOwner]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /certora/harnesses/PausableHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../src/contracts/permissions/Pausable.sol"; 5 | 6 | contract PausableHarness is Pausable { 7 | // getters 8 | function isPauser(address pauser) external view returns (bool) { 9 | return pauserRegistry.isPauser(pauser); 10 | } 11 | 12 | function unpauser() external view returns (address) { 13 | return pauserRegistry.unpauser(); 14 | } 15 | 16 | // bitwise operations 17 | function bitwise_not(uint256 input) external pure returns (uint256) { 18 | return (~input); 19 | } 20 | 21 | function bitwise_and(uint256 input_1, uint256 input_2) external pure returns (uint256) { 22 | return (input_1 & input_2); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /certora/harnesses/StrategyManagerHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../src/contracts/core/StrategyManager.sol"; 5 | 6 | contract StrategyManagerHarness is StrategyManager { 7 | constructor(IDelegationManager _delegation, IEigenPodManager _eigenPodManager, ISlasher _slasher) 8 | StrategyManager(_delegation, _eigenPodManager, _slasher) 9 | {} 10 | 11 | function strategy_is_in_stakers_array(address staker, IStrategy strategy) public view returns (bool) { 12 | uint256 length = stakerStrategyList[staker].length; 13 | for (uint256 i = 0; i < length; ++i) { 14 | if (stakerStrategyList[staker][i] == strategy) { 15 | return true; 16 | } 17 | } 18 | return false; 19 | } 20 | 21 | function num_times_strategy_is_in_stakers_array(address staker, IStrategy strategy) public view returns (uint256) { 22 | uint256 length = stakerStrategyList[staker].length; 23 | uint256 res = 0; 24 | for (uint256 i = 0; i < length; ++i) { 25 | if (stakerStrategyList[staker][i] == strategy) { 26 | res += 1; 27 | } 28 | } 29 | return res; 30 | } 31 | 32 | // checks that stakerStrategyList[staker] contains no duplicates and that all strategies in array have nonzero shares 33 | function array_exhibits_properties(address staker) public view returns (bool) { 34 | uint256 length = stakerStrategyList[staker].length; 35 | uint256 res = 0; 36 | // loop for each strategy in array 37 | for (uint256 i = 0; i < length; ++i) { 38 | IStrategy strategy = stakerStrategyList[staker][i]; 39 | // check that staker's shares in strategy are nonzero 40 | if (stakerStrategyShares[staker][strategy] == 0) { 41 | return false; 42 | } 43 | // check that strategy is not duplicated in array 44 | if (num_times_strategy_is_in_stakers_array(staker, strategy) != 1) { 45 | return false; 46 | } 47 | } 48 | return true; 49 | } 50 | 51 | function totalShares(address strategy) public view returns (uint256) { 52 | return IStrategy(strategy).totalShares(); 53 | } 54 | 55 | function get_stakerStrategyShares(address staker, IStrategy strategy) public view returns (uint256) { 56 | return stakerStrategyShares[staker][strategy]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /certora/harnesses/StructuredLinkedListHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "../../src/contracts/libraries/StructuredLinkedList.sol"; 6 | 7 | /** 8 | * @title StructuredLinkedList 9 | * @author Vittorio Minacori (https://github.com/vittominacori) 10 | * @dev An utility library for using sorted linked list data structures in your Solidity project. 11 | * @notice Adapted from https://github.com/vittominacori/solidity-linked-list/blob/master/contracts/StructuredLinkedList.sol 12 | */ 13 | contract StructuredLinkedListHarness { 14 | 15 | using StructuredLinkedList for StructuredLinkedList.List; 16 | 17 | StructuredLinkedList.List public listStorage; 18 | 19 | /// Getters with single value returns. 20 | function getAdjacentStrict(uint256 _node, bool _direction) public view returns (uint256 adj) { 21 | if (!nodeExists(_node)) { 22 | revert(); 23 | } else { 24 | adj = listStorage.list[_node][_direction]; 25 | } 26 | } 27 | 28 | // Generic setters that cover all use cases 29 | function insert(uint256 _node, uint256 _new, bool _dir) public { 30 | if (_dir) { 31 | require(listStorage.insertAfter(_node, _new), "_insert failed"); 32 | } else { 33 | require(listStorage.insertBefore(_node, _new), "_insert failed"); 34 | } 35 | 36 | } 37 | 38 | function remove(uint256 _node) public { 39 | require(listStorage.remove(_node) > 0, "remove failed"); 40 | } 41 | 42 | /// View functions made public. 43 | 44 | function listExists() public view returns (bool) { 45 | return listStorage.listExists(); 46 | } 47 | 48 | function nodeExists(uint256 _node) public view returns (bool) { 49 | return listStorage.nodeExists(_node); 50 | } 51 | 52 | function sizeOf() public view returns (uint256) { 53 | return listStorage.sizeOf(); 54 | } 55 | 56 | function getHead() public view returns (uint256) { 57 | return listStorage.getHead(); 58 | } 59 | 60 | function getNode(uint256 _node) public view returns (bool, uint256, uint256) { 61 | return listStorage.getNode(_node); 62 | } 63 | 64 | function getAdjacent(uint256 _node, bool _direction) public view returns (bool, uint256) { 65 | return listStorage.getAdjacent(_node, _direction); 66 | } 67 | 68 | function getNextNode(uint256 _node) public view returns (bool, uint256) { 69 | return listStorage.getNextNode(_node); 70 | } 71 | 72 | function getPreviousNode(uint256 _node) public view returns (bool, uint256) { 73 | return listStorage.getPreviousNode(_node); 74 | } 75 | 76 | /// Setters made public that don't add extra functionality 77 | /* commented out as some invariants require a specific preserve block for each function and creating one for each of these functions is redundant 78 | function insertAfter(uint256 _node, uint256 _new) public returns (bool) { 79 | return listStorage.insertAfter(_node, _new); 80 | } 81 | 82 | function insertBefore(uint256 _node, uint256 _new) public returns (bool) { 83 | return listStorage.insertBefore(_node, _new); 84 | } 85 | 86 | function pushFront(uint256 _node) internal returns (bool) { 87 | return listStorage.pushFront(_node); 88 | } 89 | 90 | function pushBack(uint256 _node) public returns (bool) { 91 | return listStorage.pushBack(_node); 92 | } 93 | 94 | function popFront() public returns (uint256) { 95 | return listStorage.popFront(); 96 | } 97 | 98 | function popBack() public returns (uint256) { 99 | return listStorage.popBack(); 100 | } 101 | */ 102 | 103 | } -------------------------------------------------------------------------------- /certora/scripts/core/verifyDelegationManager.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.12 7 | 8 | certoraRun certora/harnesses/DelegationManagerHarness.sol \ 9 | lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol \ 10 | src/contracts/pods/EigenPodManager.sol src/contracts/pods/EigenPod.sol src/contracts/strategies/StrategyBase.sol src/contracts/core/StrategyManager.sol \ 11 | src/contracts/core/Slasher.sol src/contracts/permissions/PauserRegistry.sol \ 12 | --verify DelegationManagerHarness:certora/specs/core/DelegationManager.spec \ 13 | --optimistic_loop \ 14 | --optimistic_fallback \ 15 | --optimistic_hashing \ 16 | --parametric_contracts DelegationManagerHarness \ 17 | $RULE \ 18 | --loop_iter 2 \ 19 | --packages @openzeppelin=lib/openzeppelin-contracts @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable \ 20 | --msg "DelegationManager $1 $2" \ 21 | -------------------------------------------------------------------------------- /certora/scripts/core/verifyStrategyManager.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.12 7 | 8 | certoraRun certora/harnesses/StrategyManagerHarness.sol \ 9 | lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol \ 10 | src/contracts/pods/EigenPodManager.sol src/contracts/pods/EigenPod.sol src/contracts/pods/DelayedWithdrawalRouter.sol \ 11 | src/contracts/strategies/StrategyBase.sol src/contracts/core/DelegationManager.sol \ 12 | src/contracts/core/Slasher.sol src/contracts/permissions/PauserRegistry.sol \ 13 | --verify StrategyManagerHarness:certora/specs/core/StrategyManager.spec \ 14 | --optimistic_loop \ 15 | --optimistic_fallback \ 16 | --optimistic_hashing \ 17 | --parametric_contracts StrategyManagerHarness \ 18 | $RULE \ 19 | --loop_iter 2 \ 20 | --packages @openzeppelin=lib/openzeppelin-contracts @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable \ 21 | --msg "StrategyManager $1 $2" \ 22 | -------------------------------------------------------------------------------- /certora/scripts/libraries/verifyStructuredLinkedList.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.12 7 | 8 | certoraRun certora/harnesses/StructuredLinkedListHarness.sol \ 9 | --verify StructuredLinkedListHarness:certora/specs/libraries/StructuredLinkedList.spec \ 10 | --optimistic_loop \ 11 | --optimistic_fallback \ 12 | --parametric_contracts StructuredLinkedListHarness \ 13 | $RULE \ 14 | --rule_sanity \ 15 | --loop_iter 3 \ 16 | --msg "StructuredLinkedList $1 $2" \ -------------------------------------------------------------------------------- /certora/scripts/permissions/verifyPausable.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.12 7 | 8 | certoraRun certora/harnesses/PausableHarness.sol \ 9 | src/contracts/permissions/PauserRegistry.sol \ 10 | --verify PausableHarness:certora/specs/permissions/Pausable.spec \ 11 | --optimistic_loop \ 12 | --optimistic_fallback \ 13 | --prover_args '-recursionErrorAsAssert false -recursionEntryLimit 3' \ 14 | --loop_iter 3 \ 15 | --link PausableHarness:pauserRegistry=PauserRegistry \ 16 | $RULE \ 17 | --msg "Pausable $1 $2" \ -------------------------------------------------------------------------------- /certora/scripts/pods/verifyEigenPod.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.12 7 | 8 | certoraRun certora/harnesses/EigenPodHarness.sol \ 9 | src/contracts/core/DelegationManager.sol src/contracts/pods/EigenPodManager.sol \ 10 | src/contracts/core/Slasher.sol src/contracts/permissions/PauserRegistry.sol \ 11 | src/contracts/core/StrategyManager.sol \ 12 | src/contracts/strategies/StrategyBase.sol \ 13 | lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol \ 14 | lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol \ 15 | --verify EigenPodHarness:certora/specs/pods/EigenPod.spec \ 16 | --optimistic_loop \ 17 | --prover_args '-recursionEntryLimit 3' \ 18 | --optimistic_hashing \ 19 | --parametric_contracts EigenPodHarness \ 20 | $RULE \ 21 | --loop_iter 1 \ 22 | --packages @openzeppelin=lib/openzeppelin-contracts @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable \ 23 | --msg "EigenPod $1 $2" \ 24 | -------------------------------------------------------------------------------- /certora/scripts/pods/verifyEigenPodManager.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.12 7 | 8 | certoraRun certora/harnesses/EigenPodManagerHarness.sol \ 9 | src/contracts/core/DelegationManager.sol src/contracts/pods/EigenPod.sol src/contracts/strategies/StrategyBase.sol src/contracts/core/StrategyManager.sol \ 10 | src/contracts/core/Slasher.sol src/contracts/permissions/PauserRegistry.sol \ 11 | --verify EigenPodManagerHarness:certora/specs/pods/EigenPodManager.spec \ 12 | --optimistic_loop \ 13 | --optimistic_fallback \ 14 | --optimistic_hashing \ 15 | --parametric_contracts EigenPodManagerHarness \ 16 | $RULE \ 17 | --loop_iter 3 \ 18 | --packages @openzeppelin=lib/openzeppelin-contracts @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable \ 19 | --msg "EigenPodManager $1 $2" \ 20 | -------------------------------------------------------------------------------- /certora/scripts/strategies/verifyStrategyBase.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.12 7 | 8 | certoraRun src/contracts/strategies/StrategyBase.sol \ 9 | lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol \ 10 | src/contracts/core/StrategyManager.sol \ 11 | src/contracts/permissions/PauserRegistry.sol \ 12 | src/contracts/core/Slasher.sol \ 13 | --verify StrategyBase:certora/specs/strategies/StrategyBase.spec \ 14 | --optimistic_loop \ 15 | --optimistic_fallback \ 16 | --prover_args '-recursionErrorAsAssert false -recursionEntryLimit 3' \ 17 | --loop_iter 3 \ 18 | --packages @openzeppelin=lib/openzeppelin-contracts @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable \ 19 | --link StrategyBase:strategyManager=StrategyManager \ 20 | --parametric_contracts StrategyBase \ 21 | $RULE \ 22 | --msg "StrategyBase $1 $2" \ -------------------------------------------------------------------------------- /certora/specs/permissions/Pausable.spec: -------------------------------------------------------------------------------- 1 | 2 | methods { 3 | // external calls to PauserRegistry 4 | function _.isPauser(address) external => DISPATCHER(true); 5 | function _.unpauser() external => DISPATCHER(true); 6 | 7 | // envfree functions 8 | function paused() external returns (uint256) envfree; 9 | function paused(uint8 index) external returns (bool) envfree; 10 | function pauserRegistry() external returns (address) envfree; 11 | 12 | // harnessed functions 13 | function isPauser(address) external returns (bool) envfree; 14 | function unpauser() external returns (address) envfree; 15 | function bitwise_not(uint256) external returns (uint256) envfree; 16 | function bitwise_and(uint256, uint256) external returns (uint256) envfree; 17 | } 18 | 19 | rule onlyPauserCanPauseAndOnlyUnpauserCanUnpause() { 20 | method f; 21 | env e; 22 | uint256 pausedStatusBefore = paused(); 23 | address unpauser = unpauser(); 24 | if (f.selector == sig:pause(uint256).selector) { 25 | uint256 newPausedStatus; 26 | pause(e, newPausedStatus); 27 | uint256 pausedStatusAfter = paused(); 28 | if (isPauser(e.msg.sender) && bitwise_and(pausedStatusBefore, newPausedStatus) == pausedStatusBefore) { 29 | assert(pausedStatusAfter == newPausedStatus, "pausedStatusAfter != newPausedStatus"); 30 | } else { 31 | assert(pausedStatusAfter == pausedStatusBefore, "pausedStatusAfter != pausedStatusBefore"); 32 | } 33 | } else if (f.selector == sig:pauseAll().selector) { 34 | pauseAll(e); 35 | uint256 pausedStatusAfter = paused(); 36 | if (isPauser(e.msg.sender)) { 37 | // assert(pausedStatusAfter == type(uint256).max, "pausedStatusAfter != newPausedStatus"); 38 | assert(pausedStatusAfter == 115792089237316195423570985008687907853269984665640564039457584007913129639935, 39 | "pausedStatusAfter != newPausedStatus"); 40 | } else { 41 | assert(pausedStatusAfter == pausedStatusBefore, "pausedStatusAfter != pausedStatusBefore"); 42 | } 43 | } else if (f.selector == sig:unpause(uint256).selector) { 44 | uint256 newPausedStatus; 45 | unpause(e, newPausedStatus); 46 | uint256 pausedStatusAfter = paused(); 47 | if (e.msg.sender == unpauser && bitwise_and(bitwise_not(pausedStatusBefore), bitwise_not(newPausedStatus)) == bitwise_not(pausedStatusBefore)) { 48 | assert(pausedStatusAfter == newPausedStatus, "pausedStatusAfter != newPausedStatus"); 49 | } else { 50 | assert(pausedStatusAfter == pausedStatusBefore, "pausedStatusAfter != pausedStatusBefore"); 51 | } 52 | } else { 53 | calldataarg arg; 54 | f(e,arg); 55 | uint256 pausedStatusAfter = paused(); 56 | assert(pausedStatusAfter == pausedStatusBefore, "pausedStatusAfter != pausedStatusBefore"); 57 | } 58 | } -------------------------------------------------------------------------------- /certora/specs/strategies/StrategyBase.spec: -------------------------------------------------------------------------------- 1 | using StrategyManager as strategyManager; 2 | methods { 3 | // external calls to StrategyManager 4 | function _.stakerStrategyShares(address, address) external => DISPATCHER(true); 5 | 6 | // external calls to PauserRegistry 7 | function _.isPauser(address) external => DISPATCHER(true); 8 | function _.unpauser() external => DISPATCHER(true); 9 | 10 | // external calls to ERC20 11 | function _.balanceOf(address) external => DISPATCHER(true); 12 | function _.transfer(address, uint256) external => DISPATCHER(true); 13 | function _.transferFrom(address, address, uint256) external => DISPATCHER(true); 14 | 15 | // external calls from StrategyManager to Slasher 16 | function _.isFrozen(address) external => DISPATCHER(true); 17 | function _.canWithdraw(address,uint32,uint256) external => DISPATCHER(true); 18 | 19 | // envfree functions 20 | function totalShares() external returns (uint256) envfree; 21 | function underlyingToken() external returns (address) envfree; 22 | function sharesToUnderlyingView(uint256) external returns (uint256) envfree; 23 | function sharesToUnderlying(uint256) external returns (uint256) envfree; 24 | function underlyingToSharesView(uint256) external returns (uint256) envfree; 25 | function underlyingToShares(uint256) external returns (uint256) envfree; 26 | function shares(address) external returns (uint256) envfree; 27 | } 28 | 29 | // // idea based on OpenZeppelin invariant -- see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/formal-verification/certora/specs/ERC20.spec#L8-L22 30 | // ghost sumOfShares() returns uint256 { 31 | // init_state axiom sumOfShares() == 0; 32 | // } 33 | 34 | // hook Sstore currentContract.strategyManager.stakerStrategyShares[KEY address staker][KEY address strategy] uint256 newValue (uint256 oldValue) STORAGE { 35 | // havoc sumOfShares assuming sumOfShares@new() == sumOfShares@old() + newValue - oldValue; 36 | // } 37 | 38 | // invariant totalSharesIsSumOfShares() 39 | // totalShares() == sumOfShares() -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | goerli: 2 | CHAIN_ID: 5 3 | EXECUTOR_MULTISIG: "0x3d9C2c2B40d890ad53E27947402e977155CD2808" 4 | FOUNDRY_FUZZ_RUNS: 1 5 | local: 6 | CHAIN_ID: 31337 7 | FOUNDRY_FUZZ_RUNS: 256 8 | -------------------------------------------------------------------------------- /docs/core/AVSDirectory.md: -------------------------------------------------------------------------------- 1 | [middleware-repo]: https://github.com/Layr-Labs/eigenlayer-middleware/ 2 | 3 | ## AVSDirectory 4 | 5 | | File | Type | Proxy | 6 | | -------- | -------- | -------- | 7 | | [`AVSDirectory.sol`](../../src/contracts/core/AVSDirectory.sol) | Singleton | Transparent proxy | 8 | 9 | The `AVSDirectory` handles interactions between AVSs and the EigenLayer core contracts. Once registered as an Operator in EigenLayer core (via the `DelegationManager`), Operators can register with one or more AVSs (via the AVS's contracts) to begin providing services to them offchain. As a part of registering with an AVS, the AVS will record this registration in the core contracts by calling into the `AVSDirectory`. 10 | 11 | For more information on AVS contracts, see the [middleware repo][middleware-repo]. 12 | 13 | Currently, the only interactions between AVSs and the core contracts is to track whether Operators are currently registered for the AVS. This is handled by two methods: 14 | * [`AVSDirectory.registerOperatorToAVS`](#registeroperatortoavs) 15 | * [`AVSDirectory.deregisterOperatorFromAVS`](#deregisteroperatorfromavs) 16 | 17 | In a future release, this contract will implement additional interactions that relate to (i) paying Operators for the services they provide and (ii) slashing Operators that misbehave. Currently, these features are not implemented. 18 | 19 | --- 20 | 21 | #### `registerOperatorToAVS` 22 | 23 | ```solidity 24 | function registerOperatorToAVS( 25 | address operator, 26 | ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature 27 | ) 28 | external 29 | onlyWhenNotPaused(PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS) 30 | ``` 31 | 32 | Allows the caller (an AVS) to register an `operator` with itself, given the provided signature is valid. 33 | 34 | *Effects*: 35 | * Sets the `operator's` status to `REGISTERED` for the AVS 36 | 37 | *Requirements*: 38 | * Pause status MUST NOT be set: `PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS` 39 | * `operator` MUST already be a registered Operator (via the `DelegationManager`) 40 | * `operator` MUST NOT already be registered with the AVS 41 | * `operatorSignature` must be a valid, unused, unexpired signature from the `operator`. The signature is an ECDSA signature by the operator over the [`OPERATOR_AVS_REGISTRATION_TYPEHASH`](../../src/contracts/core/DelegationManagerStorage.sol). Expiry is a utc timestamp in seconds. Salt is used only once per signature to prevent replay attacks. 42 | 43 | *As of M2*: 44 | * Operator registration/deregistration does not have any sort of consequences for the Operator or its shares. Eventually, this will tie into payments for services and slashing for misbehavior. 45 | 46 | #### `deregisterOperatorFromAVS` 47 | 48 | ```solidity 49 | function deregisterOperatorFromAVS( 50 | address operator 51 | ) 52 | external 53 | onlyWhenNotPaused(PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS) 54 | ``` 55 | 56 | Allows the caller (an AVS) to deregister an `operator` with itself 57 | 58 | *Effects*: 59 | * Sets the `operator's` status to `UNREGISTERED` for the AVS 60 | 61 | *Requirements*: 62 | * Pause status MUST NOT be set: `PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS` 63 | * `operator` MUST already be registered with the AVS 64 | 65 | *As of M2*: 66 | * Operator registration/deregistration does not have any sort of consequences for the Operator or its shares. Eventually, this will tie into payments for services and slashing for misbehavior. 67 | 68 | #### `cancelSalt` 69 | 70 | ```solidity 71 | function cancelSalt(bytes32 salt) external 72 | ``` 73 | 74 | Allows the caller (an Operator) to cancel a signature salt before it is used to register for an AVS. 75 | 76 | *Effects*: 77 | * Sets `operatorSaltIsSpent[msg.sender][salt]` to `true` 78 | 79 | *Requirements*: 80 | * Salt MUST NOT already be cancelled -------------------------------------------------------------------------------- /docs/images/EL_completing_queued_withdrawal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_completing_queued_withdrawal.png -------------------------------------------------------------------------------- /docs/images/EL_delegating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_delegating.png -------------------------------------------------------------------------------- /docs/images/EL_depositing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_depositing.png -------------------------------------------------------------------------------- /docs/images/EL_depositing_BeaconChainETH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_depositing_BeaconChainETH.png -------------------------------------------------------------------------------- /docs/images/EL_depositing_BeaconChainETH_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_depositing_BeaconChainETH_2.png -------------------------------------------------------------------------------- /docs/images/EL_eigenpods_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_eigenpods_architecture.png -------------------------------------------------------------------------------- /docs/images/EL_operator_registration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_operator_registration.png -------------------------------------------------------------------------------- /docs/images/EL_queuing_a_withdrawal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EL_queuing_a_withdrawal.png -------------------------------------------------------------------------------- /docs/images/EigenPods_Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/EigenPods_Architecture.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Complete Withdrawal as Shares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Staker Flow Diagrams/Complete Withdrawal as Shares.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Complete Withdrawal as Tokens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Staker Flow Diagrams/Complete Withdrawal as Tokens.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Delegating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Staker Flow Diagrams/Delegating.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Depositing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Staker Flow Diagrams/Depositing.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Partial Withdrawals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Staker Flow Diagrams/Partial Withdrawals.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Queue Withdrawal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Staker Flow Diagrams/Queue Withdrawal.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Validator Exits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Staker Flow Diagrams/Validator Exits.png -------------------------------------------------------------------------------- /docs/images/Withdrawal_Credential_Proof.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Withdrawal_Credential_Proof.png -------------------------------------------------------------------------------- /docs/images/Withdrawal_Proof.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Withdrawal_Proof.png -------------------------------------------------------------------------------- /docs/images/Withdrawal_Proof_Diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/Withdrawal_Proof_Diagram.png -------------------------------------------------------------------------------- /docs/images/middleware_outline_doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/middleware_outline_doc.png -------------------------------------------------------------------------------- /docs/images/operator_deregister.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/operator_deregister.png -------------------------------------------------------------------------------- /docs/images/operator_opting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/operator_opting.png -------------------------------------------------------------------------------- /docs/images/samplemerkle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/samplemerkle.png -------------------------------------------------------------------------------- /docs/images/slashing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/slashing.png -------------------------------------------------------------------------------- /docs/images/staker_opting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/staker_opting.png -------------------------------------------------------------------------------- /docs/images/staker_withdrawing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/staker_withdrawing.png -------------------------------------------------------------------------------- /docs/images/staterootproof.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/staterootproof.png -------------------------------------------------------------------------------- /docs/images/three_middlewares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/three_middlewares.png -------------------------------------------------------------------------------- /docs/images/three_middlewares_withdrawal_queued.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/three_middlewares_withdrawal_queued.png -------------------------------------------------------------------------------- /docs/images/withdrawal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenfoundation/eigenlayer-contracts/dbfa12128a41341b936f3e8da5d6da58c6233877/docs/images/withdrawal.png -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | fs_permissions = [{ access = "read-write", path = "./"}] 6 | gas_reports = ["*"] 7 | # ignore upgrade testing in scripts by default 8 | no_match_test = "queueUpgrade" 9 | 10 | # A list of ignored solc error codes 11 | 12 | # Enables or disables the optimizer 13 | optimizer = true 14 | # The number of optimizer runs 15 | optimizer_runs = 200 16 | # Whether or not to use the Yul intermediate representation compilation pipeline 17 | via_ir = false 18 | # Override the Solidity version (this overrides `auto_detect_solc`) 19 | solc_version = '0.8.12' 20 | 21 | [rpc_endpoints] 22 | mainnet = "${RPC_MAINNET}" 23 | holesky = "${RPC_HOLESKY}" 24 | 25 | [fmt] 26 | bracket_spacing = false 27 | int_types = "long" 28 | line_length = 120 29 | multiline_func_header = "params_first" 30 | number_underscore = "thousands" 31 | quote_style = "double" 32 | tab_width = 4 33 | 34 | # See more config options https://github.com/gakonst/foundry/tree/master/config 35 | 36 | [profile.forktest.fuzz] 37 | runs=20 -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "hardhat-preprocessor"; 2 | import fs from "fs"; 3 | import "solidity-docgen"; 4 | 5 | function getRemappings() { 6 | return fs 7 | .readFileSync("remappings.txt", "utf8") 8 | .split("\n") 9 | .filter(Boolean) // remove empty lines 10 | .map((line) => line.trim().split("=")); 11 | } 12 | 13 | /** @type import('hardhat/config').HardhatUserConfig */ 14 | module.exports = { 15 | solidity: "0.8.12", 16 | preprocess: { 17 | eachLine: (hre) => ({ 18 | transform: (line: string) => { 19 | if (line.match(/^\s*import /i)) { 20 | for (const [from, to] of getRemappings()) { 21 | if (line.includes(from)) { 22 | line = line.replace(from, to); 23 | break; 24 | } 25 | } 26 | } 27 | return line; 28 | }, 29 | }), 30 | }, 31 | paths: { 32 | sources: "./src/contracts", 33 | cache: "./cache_hardhat", 34 | }, 35 | docgen: { 36 | outputDir: "docs/docgen", 37 | pages: "files" 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /mythril.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "remappings": [ 3 | "forge-std/=lib/forge-std/src/", 4 | "@openzeppelin/=lib/openzeppelin-contracts/", 5 | "@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable/" 6 | ], 7 | "optimizer": { 8 | "enabled": true, 9 | "runs": 200 10 | } 11 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eigenlayer-contracts", 3 | "version": "1.0.0", 4 | "description": " # EigenLayer EigenLayer (formerly 'EigenLayr') is a set of smart contracts deployed on Ethereum that enable restaking of assets to secure new services. At present, this repository contains *both* the contracts for EigenLayer *and* a set of general \"middleware\" contracts, designed to be reuseable across different applications built on top of EigenLayer.", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs", 8 | "lib": "lib" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/Layr-Labs/eigenlayer-contracts.git" 16 | }, 17 | "author": "", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/Layr-Labs/eigenlayer-contracts/issues" 21 | }, 22 | "homepage": "https://github.com/Layr-Labs/eigenlayer-contracts#readme", 23 | "devDependencies": { 24 | "@commitlint/cli": "^18.2.0", 25 | "@commitlint/config-conventional": "^18.1.0", 26 | "@types/yargs": "^17.0.28", 27 | "chalk": "^4.1.0", 28 | "dotenv": "^16.3.1", 29 | "fs": "^0.0.1-security", 30 | "hardhat": "^2.12.4", 31 | "hardhat-preprocessor": "^0.1.5", 32 | "husky": "^8.0.3", 33 | "ts-node": "^10.9.1", 34 | "typescript": "^4.9.4", 35 | "yargs": "^17.7.2" 36 | }, 37 | "dependencies": { 38 | "solidity-docgen": "^0.6.0-beta.32" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | @openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable/ 2 | @openzeppelin/=lib/openzeppelin-contracts/ 3 | ds-test/=lib/ds-test/src/ 4 | forge-std/=lib/forge-std/src/ 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certora-cli==3.6.4 -------------------------------------------------------------------------------- /script/admin/mainnet/Mainnet_Unpause_Deposits.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../utils/ExistingDeploymentParser.sol"; 5 | import "../../utils/TimelockEncoding.sol"; 6 | 7 | // forge script script/admin/mainnet/Mainnet_Unpause_Deposits.s.sol:Mainnet_Unpause_Deposits --fork-url $RPC_MAINNET -vvvv 8 | contract Mainnet_Unpause_Deposits is ExistingDeploymentParser, TimelockEncoding { 9 | Vm cheats = Vm(HEVM_ADDRESS); 10 | 11 | // Tues Apr 16 2024 12:00:00 GMT-0700 (Pacific Daylight Time) 12 | uint256 timelockEta = 1713250800; 13 | 14 | function run() external virtual { 15 | _parseDeployedContracts("script/output/mainnet/M1_deployment_mainnet_2023_6_9.json"); 16 | 17 | bytes memory final_calldata_to_executor_multisig = encodeForExecutor({ 18 | // call to executor will be from the timelock 19 | from: timelock, 20 | // performing single pause operation 21 | to: address(strategyManager), 22 | // value to send in tx 23 | value: 0, 24 | // calldata for the operation 25 | data: abi.encodeWithSelector(Pausable.unpause.selector, 0), 26 | // operation type (for performing single operation) 27 | operation: ISafe.Operation.Call 28 | }); 29 | 30 | (bytes memory calldata_to_timelock_queuing_action, bytes memory calldata_to_timelock_executing_action) = encodeForTimelock({ 31 | // address to be called from the timelock 32 | to: executorMultisig, 33 | // value to send in tx 34 | value: 0, 35 | // calldata for the operation 36 | data: final_calldata_to_executor_multisig, 37 | // time at which the tx will become executable 38 | timelockEta: timelockEta 39 | }); 40 | 41 | bytes32 expectedTxHash = getTxHash({ 42 | target: executorMultisig, 43 | _value: 0, 44 | _data: final_calldata_to_executor_multisig, 45 | eta: timelockEta 46 | }); 47 | emit log_named_bytes32("expectedTxHash", expectedTxHash); 48 | 49 | cheats.prank(operationsMultisig); 50 | (bool success, ) = timelock.call(calldata_to_timelock_queuing_action); 51 | require(success, "call to timelock queuing action failed"); 52 | 53 | require(ITimelock(timelock).queuedTransactions(expectedTxHash), "expectedTxHash not queued"); 54 | 55 | // test performing the upgrade 56 | cheats.warp(timelockEta); 57 | cheats.prank(operationsMultisig); 58 | (success, ) = timelock.call(calldata_to_timelock_executing_action); 59 | require(success, "call to timelock executing action failed"); 60 | 61 | // Check correctness after upgrade 62 | require(strategyManager.paused() == 0, "unpausing was not completed correctly"); 63 | } 64 | 65 | function getTxHash(address target, uint256 _value, bytes memory _data, uint256 eta) public pure returns (bytes32) { 66 | // empty bytes 67 | bytes memory signature; 68 | bytes32 txHash = keccak256(abi.encode(target, _value, signature, _data, eta)); 69 | return txHash; 70 | } 71 | } -------------------------------------------------------------------------------- /script/configs/devnet/M1_deploy_devnet.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "multisig_addresses": { 3 | "communityMultisig": "0xB499Be0490Fe8e9382101b8a747BCC88e346f84b", 4 | "operationsMultisig": "0x040353E9d057689b77DF275c07FFe1A46b98a4a6", 5 | "executorMultisig": "0x3d9C2c2B40d890ad53E27947402e977155CD2808", 6 | "timelock": "0xA7e72a0564ebf25Fa082Fc27020225edeAF1796E" 7 | }, 8 | "strategies": [ 9 | { 10 | "token_address": "0x", 11 | "token_name": "Mock WETH (MOCK WETH)", 12 | "token_symbol": "WETH" 13 | }, 14 | { 15 | "token_address": "0x", 16 | "token_name": "Mock EIGEN (MOCK EIGEN)", 17 | "token_symbol": "EIGEN" 18 | } 19 | ], 20 | "strategyManager": 21 | { 22 | "withdrawalDelayBlocks": 10, 23 | "init_paused_status": 0, 24 | "init_withdrawal_delay_blocks": 10 25 | }, 26 | "eigenPod": 27 | { 28 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 50400, 29 | "REQUIRED_BALANCE_WEI": "32000000000000000000" 30 | }, 31 | "eigenPodManager": 32 | { 33 | "init_paused_status": 30 34 | }, 35 | "delayedWithdrawalRouter": 36 | { 37 | "init_paused_status": 0, 38 | "init_withdrawal_delay_blocks": 50400 39 | }, 40 | "slasher": 41 | { 42 | "init_paused_status": "115792089237316195423570985008687907853269984665640564039457584007913129639935" 43 | }, 44 | "delegation": 45 | { 46 | "init_paused_status": "115792089237316195423570985008687907853269984665640564039457584007913129639935" 47 | } 48 | } -------------------------------------------------------------------------------- /script/configs/devnet/M2_deploy_from_scratch.anvil.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "maintainer": "samlaf@eigenlabs.org", 3 | "multisig_addresses": { 4 | "operationsMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 5 | "pauserMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 6 | "executorMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" 7 | }, 8 | "strategies": [], 9 | "strategyManager": { 10 | "init_paused_status": 0, 11 | "init_withdrawal_delay_blocks": 1 12 | }, 13 | "eigenPod": { 14 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 1, 15 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "32000000000" 16 | }, 17 | "eigenPodManager": { 18 | "init_paused_status": 30 19 | }, 20 | "delayedWithdrawalRouter": { 21 | "init_paused_status": 0, 22 | "init_withdrawal_delay_blocks": 1 23 | }, 24 | "slasher": { 25 | "init_paused_status": 0 26 | }, 27 | "delegation": { 28 | "init_paused_status": 0, 29 | "init_withdrawal_delay_blocks": 1 30 | }, 31 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa" 32 | } -------------------------------------------------------------------------------- /script/configs/goerli/M1_deploy_goerli.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "multisig_addresses": { 3 | "pauserMultisig": "0x040353E9d057689b77DF275c07FFe1A46b98a4a6", 4 | "communityMultisig": "0x37bAFb55BC02056c5fD891DFa503ee84a97d89bF", 5 | "operationsMultisig": "0x040353E9d057689b77DF275c07FFe1A46b98a4a6", 6 | "executorMultisig": "0x3d9C2c2B40d890ad53E27947402e977155CD2808", 7 | "timelock": "0xA7e72a0564ebf25Fa082Fc27020225edeAF1796E" 8 | }, 9 | "strategies": [ 10 | { 11 | "token_address": "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", 12 | "token_name": "Wrapped Ether", 13 | "token_symbol": "WETH" 14 | }, 15 | { 16 | "token_address": "0x6320cd32aa674d2898a68ec82e869385fc5f7e2f", 17 | "token_name": "Wrapped liquid staked Ether 2.0", 18 | "token_symbol": "wstETH" 19 | }, 20 | { 21 | "token_address": "0x178e141a0e3b34152f73ff610437a7bf9b83267a", 22 | "token_name": "Rocket Pool ETH", 23 | "token_symbol": "rETH" 24 | }, 25 | { 26 | "token_address": "0x", 27 | "token_name": "Test Staked Ether", 28 | "token_symbol": "tsETH" 29 | } 30 | ], 31 | "strategyManager": { 32 | "init_paused_status": 0, 33 | "init_withdrawal_delay_blocks": 10 34 | }, 35 | "eigenPod": { 36 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 50400, 37 | "REQUIRED_BALANCE_WEI": "31000000000000000000" 38 | }, 39 | "eigenPodManager": { 40 | "init_paused_status": 30 41 | }, 42 | "delayedWithdrawalRouter": { 43 | "init_paused_status": 0, 44 | "init_withdrawal_delay_blocks": 10 45 | }, 46 | "slasher": { 47 | "init_paused_status": "115792089237316195423570985008687907853269984665640564039457584007913129639935" 48 | }, 49 | "delegation": { 50 | "init_paused_status": "115792089237316195423570985008687907853269984665640564039457584007913129639935" 51 | }, 52 | "ethPOSDepositAddress": "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b" 53 | } 54 | 55 | -------------------------------------------------------------------------------- /script/configs/holesky/Holesky_current_deployment.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", 4 | "avsDirectoryImplementation": "0xEF5BA995Bc7722fd1e163edF8Dc09375de3d3e3a", 5 | "baseStrategyImplementation": "0xFb83e1D133D0157775eC4F19Ff81478Df1103305", 6 | "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", 7 | "delayedWithdrawalRouter": "0x642c646053eaf2254f088e9019ACD73d9AE0FA32", 8 | "delayedWithdrawalRouterImplementation": "0xcE8b8D99773a718423F8040a6e52c06a4ce63407", 9 | "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", 10 | "delegationManagerImplementation": "0x83f8F8f0BB125F7870F6bfCf76853f874C330D76", 11 | "eigenLayerPauserReg": "0x85Ef7299F8311B25642679edBF02B62FA2212F06", 12 | "eigenLayerProxyAdmin": "0xDB023566064246399b4AE851197a97729C93A6cf", 13 | "eigenPodBeacon": "0x7261C2bd75a7ACE1762f6d7FAe8F63215581832D", 14 | "eigenPodImplementation": "0xe98f9298344527608A1BCC23907B8145F9Cb641c", 15 | "eigenPodManager": "0x30770d7E3e71112d7A6b7259542D1f680a70e315", 16 | "eigenPodManagerImplementation": "0x5265C162f7d5F3fE3175a78828ab16bf5E324a7B", 17 | "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", 18 | "slasher": "0xcAe751b75833ef09627549868A04E32679386e7C", 19 | "slasherImplementation": "0x99715D255E34a39bE9943b82F281CA734bcF345A", 20 | "strategies": { 21 | "WETH": "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", 22 | "rETH": "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", 23 | "stETH": "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", 24 | "lsETH": "0x05037A81BD7B4C9E0F7B430f1F2A22c31a2FD943", 25 | "frxETH": "0x15F70a41Afe34020B3B16079010D3e88c4A85daf", 26 | "ETHx": "0x31B6F59e1627cEfC9fA174aD03859fC337666af7", 27 | "osETH": "0x46281E3B7fDcACdBa44CADf069a94a588Fd4C6Ef", 28 | "cbETH": "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6" 29 | }, 30 | "strategyAddresses": [ 31 | "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", 32 | "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", 33 | "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", 34 | "0x05037A81BD7B4C9E0F7B430f1F2A22c31a2FD943", 35 | "0x15F70a41Afe34020B3B16079010D3e88c4A85daf", 36 | "0x31B6F59e1627cEfC9fA174aD03859fC337666af7", 37 | "0x46281E3B7fDcACdBa44CADf069a94a588Fd4C6Ef", 38 | "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6" 39 | ], 40 | "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", 41 | "strategyManagerImplementation": "0x59f766A603C53f3AC8Be43bBe158c1519b193a18" 42 | }, 43 | "numStrategies": 8, 44 | "chainInfo": { 45 | "chainId": 17000, 46 | "deploymentBlock": 1167041 47 | }, 48 | "parameters": { 49 | "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", 50 | "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", 51 | "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", 52 | "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", 53 | "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /script/configs/holesky/M2_deploy_from_scratch.holesky.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "chainInfo": { 3 | "chainId": 17000 4 | }, 5 | "multisig_addresses": { 6 | "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", 7 | "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", 8 | "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", 9 | "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", 10 | "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" 11 | }, 12 | "strategies": { 13 | "numStrategies": 0, 14 | "MAX_PER_DEPOSIT": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 15 | "MAX_TOTAL_DEPOSITS": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 16 | "strategiesToDeploy": [] 17 | }, 18 | "strategyManager": { 19 | "init_strategy_whitelister": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", 20 | "init_paused_status": 0 21 | }, 22 | "delegationManager": { 23 | "init_paused_status": 0, 24 | "init_minWithdrawalDelayBlocks": 50400 25 | }, 26 | "avsDirectory": { 27 | "init_paused_status": 0 28 | }, 29 | "slasher": { 30 | "init_paused_status": 0 31 | }, 32 | "eigenPod": { 33 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": 32000000000, 34 | "GENESIS_TIME": 1695902400 35 | }, 36 | "eigenPodManager": { 37 | "init_paused_status": 0, 38 | "deneb_fork_timestamp": "1707305664" 39 | }, 40 | "delayedWithdrawalRouter": { 41 | "init_paused_status": 0, 42 | "init_withdrawalDelayBlocks": 50400 43 | }, 44 | "ethPOSDepositAddress": "0x4242424242424242424242424242424242424242", 45 | "beaconOracleAddress": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25" 46 | } -------------------------------------------------------------------------------- /script/configs/holesky/M2_deploy_preprod.holesky.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "chainInfo": { 3 | "chainId": 17000 4 | }, 5 | "multisig_addresses": { 6 | "pauserMultisig": "0x0000000000000000000000000000000000000000", 7 | "communityMultisig": "0x0000000000000000000000000000000000000000", 8 | "operationsMultisig": "0x0000000000000000000000000000000000000000", 9 | "executorMultisig": "0x0000000000000000000000000000000000000000", 10 | "timelock": "0x0000000000000000000000000000000000000000" 11 | }, 12 | "numStrategies": 2, 13 | "strategies": { 14 | "numStrategies": 2, 15 | "MAX_PER_DEPOSIT": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 16 | "MAX_TOTAL_DEPOSITS": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 17 | "strategiesToDeploy": [ 18 | { 19 | "token_address": "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", 20 | "token_name": "Liquid staked Ether 2.0", 21 | "token_symbol": "stETH" 22 | }, 23 | { 24 | "token_address": "0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1", 25 | "token_name": "Rocket Pool ETH", 26 | "token_symbol": "rETH" 27 | } 28 | ] 29 | }, 30 | "strategyManager": { 31 | "init_strategy_whitelister": "0x0000000000000000000000000000000000000000", 32 | "init_paused_status": 0 33 | }, 34 | "delegationManager": { 35 | "init_paused_status": 0, 36 | "init_minWithdrawalDelayBlocks": 50400 37 | }, 38 | "avsDirectory": { 39 | "init_paused_status": 0 40 | }, 41 | "slasher": { 42 | "init_paused_status": 0 43 | }, 44 | "eigenPod": { 45 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": 32000000000000000000, 46 | "GENESIS_TIME": 1695902400 47 | }, 48 | "eigenPodManager": { 49 | "init_paused_status": 0, 50 | "deneb_fork_timestamp": "1707305664" 51 | }, 52 | "delayedWithdrawalRouter": { 53 | "init_paused_status": 0, 54 | "init_withdrawalDelayBlocks": 50400 55 | }, 56 | "ethPOSDepositAddress": "0x4242424242424242424242424242424242424242", 57 | "beaconOracleAddress": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25" 58 | } -------------------------------------------------------------------------------- /script/configs/mainnet/M1_deploy_mainnet.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "multisig_addresses": { 3 | "communityMultisig": "0xFEA47018D632A77bA579846c840d5706705Dc598", 4 | "operationsMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 5 | "pauserMultisig": "0x5050389572f2d220ad927CcbeA0D406831012390", 6 | "executorMultisig": "0x369e6F597e22EaB55fFb173C6d9cD234BD699111", 7 | "timelock": "0xA6Db1A8C5a981d1536266D2a393c5F8dDb210EAF" 8 | }, 9 | "strategies": [ 10 | { 11 | "token_address": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", 12 | "token_name": "Coinbase Wrapped Staked ETH", 13 | "token_symbol": "cbETH", 14 | "max_per_deposit": 0, 15 | "max_deposits": 0 16 | }, 17 | { 18 | "token_address": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84", 19 | "token_name": "Liquid staked Ether 2.0", 20 | "token_symbol": "stETH", 21 | "max_per_deposit": 0, 22 | "max_deposits": 0 23 | }, 24 | { 25 | "token_address": "0xae78736Cd615f374D3085123A210448E74Fc6393", 26 | "token_name": "Rocket Pool ETH", 27 | "token_symbol": "rETH", 28 | "max_per_deposit": 0, 29 | "max_deposits": 0 30 | } 31 | ], 32 | "strategyManager": 33 | { 34 | "init_paused_status": 0, 35 | "init_withdrawal_delay_blocks": 50400 36 | }, 37 | "eigenPod": 38 | { 39 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 50400, 40 | "REQUIRED_BALANCE_WEI": "32000000000000000000" 41 | }, 42 | "eigenPodManager": 43 | { 44 | "max_pods": 0, 45 | "init_paused_status": 30 46 | }, 47 | "delayedWithdrawalRouter": 48 | { 49 | "init_paused_status": 0, 50 | "init_withdrawal_delay_blocks": 50400 51 | }, 52 | "slasher": 53 | { 54 | "init_paused_status": "115792089237316195423570985008687907853269984665640564039457584007913129639935" 55 | }, 56 | "delegation": 57 | { 58 | "init_paused_status": "115792089237316195423570985008687907853269984665640564039457584007913129639935" 59 | }, 60 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa" 61 | } -------------------------------------------------------------------------------- /script/configs/mainnet/M2_deploy_from_scratch.mainnet.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "multisig_addresses": { 3 | "operationsMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 4 | "pauserMultisig": "0x5050389572f2d220ad927CcbeA0D406831012390", 5 | "executorMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90" 6 | }, 7 | "strategies": [ 8 | { 9 | "token_address": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", 10 | "token_name": "Coinbase Wrapped Staked ETH", 11 | "token_symbol": "cbETH", 12 | "max_per_deposit": 0, 13 | "max_deposits": 0 14 | }, 15 | { 16 | "token_address": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84", 17 | "token_name": "Liquid staked Ether 2.0", 18 | "token_symbol": "stETH", 19 | "max_per_deposit": 0, 20 | "max_deposits": 0 21 | }, 22 | { 23 | "token_address": "0xae78736Cd615f374D3085123A210448E74Fc6393", 24 | "token_name": "Rocket Pool ETH", 25 | "token_symbol": "rETH", 26 | "max_per_deposit": 0, 27 | "max_deposits": 0 28 | } 29 | ], 30 | "strategyManager": 31 | { 32 | "init_paused_status": 0, 33 | "init_withdrawal_delay_blocks": 50400 34 | }, 35 | "eigenPod": 36 | { 37 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 50400, 38 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "32000000000" 39 | }, 40 | "eigenPodManager": 41 | { 42 | "max_pods": 0, 43 | "init_paused_status": 30 44 | }, 45 | "delayedWithdrawalRouter": 46 | { 47 | "init_paused_status": 0, 48 | "init_withdrawal_delay_blocks": 50400 49 | }, 50 | "slasher": 51 | { 52 | "init_paused_status": 0 53 | }, 54 | "delegation": 55 | { 56 | "init_paused_status": 0, 57 | "init_withdrawal_delay_blocks": 50400 58 | }, 59 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa" 60 | } -------------------------------------------------------------------------------- /script/configs/mainnet/M2_mainnet_upgrade.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "chainInfo": { 3 | "chainId": 1 4 | }, 5 | "multisig_addresses": { 6 | "communityMultisig": "0xFEA47018D632A77bA579846c840d5706705Dc598", 7 | "executorMultisig": "0x369e6F597e22EaB55fFb173C6d9cD234BD699111", 8 | "operationsMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 9 | "pauserMultisig": "0x5050389572f2d220ad927CcbeA0D406831012390", 10 | "timelock": "0xA6Db1A8C5a981d1536266D2a393c5F8dDb210EAF" 11 | }, 12 | "strategies": { 13 | "numStrategies": 0, 14 | "MAX_PER_DEPOSIT": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 15 | "MAX_TOTAL_DEPOSITS": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 16 | "strategiesToDeploy": [] 17 | }, 18 | "strategyManager": { 19 | "init_strategy_whitelister": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 20 | "init_paused_status": 1 21 | }, 22 | "delegationManager": { 23 | "init_paused_status": 0, 24 | "init_minWithdrawalDelayBlocks": 50400 25 | }, 26 | "avsDirectory": { 27 | "init_paused_status": 0 28 | }, 29 | "slasher": { 30 | "init_paused_status": 115792089237316195423570985008687907853269984665640564039457584007913129639935 31 | }, 32 | "eigenPod": { 33 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": 32000000000, 34 | "GENESIS_TIME": 1606824023 35 | }, 36 | "eigenPodManager": { 37 | "init_paused_status": 0, 38 | "deneb_fork_timestamp": "1710338135" 39 | }, 40 | "delayedWithdrawalRouter": { 41 | "init_paused_status": 0, 42 | "init_withdrawalDelayBlocks": 50400 43 | }, 44 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa", 45 | "beaconOracleAddress": "0x343907185b71aDF0eBa9567538314396aa985442" 46 | } -------------------------------------------------------------------------------- /script/configs/mainnet/Mainnet_current_deployment.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "avsDirectory": "0x0000000000000000000000000000000000000000", 4 | "avsDirectoryImplementation": "0x0000000000000000000000000000000000000000", 5 | "beaconOracle": "0x343907185b71aDF0eBa9567538314396aa985442", 6 | "baseStrategyImplementation": "0xdfdA04f980bE6A64E3607c95Ca26012Ab9aA46d3", 7 | "delayedWithdrawalRouter": "0x7Fe7E9CC0F274d2435AD5d56D5fa73E47F6A23D8", 8 | "delayedWithdrawalRouterImplementation": "0x44Bcb0E01CD0C5060D4Bb1A07b42580EF983E2AF", 9 | "delegationManager": "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A", 10 | "delegationManagerImplementation": "0xf97E97649Da958d290e84E6D571c32F4b7F475e4", 11 | "eigenLayerPauserReg": "0x0c431C66F4dE941d089625E5B423D00707977060", 12 | "eigenLayerProxyAdmin": "0x8b9566AdA63B64d1E1dcF1418b43fd1433b72444", 13 | "eigenPodBeacon": "0x5a2a4F2F3C18f09179B6703e63D9eDD165909073", 14 | "eigenPodImplementation": "0x5c86e9609fbBc1B754D0FD5a4963Fdf0F5b99dA7", 15 | "eigenPodManager": "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338", 16 | "eigenPodManagerImplementation": "0xEB86a5c40FdE917E6feC440aBbCDc80E3862e111", 17 | "emptyContract": "0x1f96861fEFa1065a5A96F20Deb6D8DC3ff48F7f9", 18 | "slasher": "0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd", 19 | "slasherImplementation": "0xef31c292801f24f16479DD83197F1E6AeBb8d6d8", 20 | "strategyManager": "0x858646372CC42E1A627fcE94aa7A7033e7CF075A", 21 | "strategyManagerImplementation": "0x5d25EEf8CfEdaA47d31fE2346726dE1c21e342Fb", 22 | "strategies": { 23 | "stETH": "0x93c4b944D05dfe6df7645A86cd2206016c51564D", 24 | "rETH": "0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2", 25 | "cbETH": "0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc", 26 | "ETHx": "0x9d7eD45EE2E8FC5482fa2428f15C971e6369011d", 27 | "ankrETH": "0x13760F50a9d7377e4F20CB8CF9e4c26586c658ff", 28 | "oETH": "0xa4C637e0F704745D182e4D38cAb7E7485321d059", 29 | "osETH": "0x57ba429517c3473B6d34CA9aCd56c0e735b94c02", 30 | "swETH": "0x0Fe4F44beE93503346A3Ac9EE5A26b130a5796d6", 31 | "wBETH": "0x7CA911E83dabf90C90dD3De5411a10F1A6112184", 32 | "sfrxETH": "0x8CA7A5d6f3acd3A7A8bC468a8CD0FB14B6BD28b6", 33 | "lsETH": "0xAe60d8180437b5C34bB956822ac2710972584473", 34 | "mETH": "0x298aFB19A105D59E74658C4C334Ff360BadE6dd2" 35 | }, 36 | "strategyAddresses": [ 37 | "0x93c4b944D05dfe6df7645A86cd2206016c51564D", 38 | "0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2", 39 | "0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc", 40 | "0x9d7eD45EE2E8FC5482fa2428f15C971e6369011d", 41 | "0x13760F50a9d7377e4F20CB8CF9e4c26586c658ff", 42 | "0xa4C637e0F704745D182e4D38cAb7E7485321d059", 43 | "0x57ba429517c3473B6d34CA9aCd56c0e735b94c02", 44 | "0x0Fe4F44beE93503346A3Ac9EE5A26b130a5796d6", 45 | "0x7CA911E83dabf90C90dD3De5411a10F1A6112184", 46 | "0x8CA7A5d6f3acd3A7A8bC468a8CD0FB14B6BD28b6", 47 | "0xAe60d8180437b5C34bB956822ac2710972584473", 48 | "0x298aFB19A105D59E74658C4C334Ff360BadE6dd2" 49 | ] 50 | }, 51 | "numStrategies": 12, 52 | "chainInfo": { 53 | "chainId": 1, 54 | "deploymentBlock": 17445559 55 | }, 56 | "parameters": { 57 | "communityMultisig": "0xFEA47018D632A77bA579846c840d5706705Dc598", 58 | "executorMultisig": "0x369e6F597e22EaB55fFb173C6d9cD234BD699111", 59 | "operationsMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 60 | "pauserMultisig": "0x5050389572f2d220ad927CcbeA0D406831012390", 61 | "timelock": "0xA6Db1A8C5a981d1536266D2a393c5F8dDb210EAF" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /script/configs/mainnet/Mainnet_current_eigenPods.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "chainInfo": { 3 | "chainId": 1, 4 | "deploymentBlock": 19285000 5 | }, 6 | "eigenPods": { 7 | "multiValidators": [ 8 | "0x2641c2ded63a0c640629f5edf1189e0f53c06561", 9 | "0xa345dcb63f984ed1c6d1a8901e0cdbd13b2b4d19", 10 | "0x7fb3d3801883341a02ddd1beb2e624a278e87930", 11 | "0x2f5cb052d397d0b628299a9d4cfc49a5019c4170", 12 | "0xdb678e1056acd7db74507b921a6295c3d586ece9" 13 | ], 14 | "singleValidators": [ 15 | "0xc149531419db43b8cc0d4f2f2ce1700ae46f4c8a", 16 | "0x61340dcc5aef625ded27f21e5068916ad334dad0", 17 | "0x722e2350a55e6b617e66983b4b91d47fe9e9403e", 18 | "0x49213606dc1953eae3b733187fed9e7307edd55b", 19 | "0xa8fc63e72288b79009f7b5952760114513efc700" 20 | ], 21 | "inActive": [ 22 | "0xcbb42f0d320056453c497867119814c59615daeb", 23 | "0xd6e50e7f6250ca26d5d5033138ad09cfe2aaeacb", 24 | "0xe18c1447804563af9647cf8c879f35ced8172c1f", 25 | "0x38de067514c77fed61630bb77ecc24f2adb73ff4", 26 | "0xc3f9bbd74ded6b2bc2ff8b5c693fcbabf2e24efd" 27 | ], 28 | "allEigenPods": [ 29 | "0x2641c2ded63a0c640629f5edf1189e0f53c06561", 30 | "0xa345dcb63f984ed1c6d1a8901e0cdbd13b2b4d19", 31 | "0x7fb3d3801883341a02ddd1beb2e624a278e87930", 32 | "0x2f5cb052d397d0b628299a9d4cfc49a5019c4170", 33 | "0xdb678e1056acd7db74507b921a6295c3d586ece9", 34 | "0xc149531419db43b8cc0d4f2f2ce1700ae46f4c8a", 35 | "0x61340dcc5aef625ded27f21e5068916ad334dad0", 36 | "0x722e2350a55e6b617e66983b4b91d47fe9e9403e", 37 | "0x49213606dc1953eae3b733187fed9e7307edd55b", 38 | "0xa8fc63e72288b79009f7b5952760114513efc700", 39 | "0xcbb42f0d320056453c497867119814c59615daeb", 40 | "0xd6e50e7f6250ca26d5d5033138ad09cfe2aaeacb", 41 | "0xe18c1447804563af9647cf8c879f35ced8172c1f", 42 | "0x38de067514c77fed61630bb77ecc24f2adb73ff4", 43 | "0xc3f9bbd74ded6b2bc2ff8b5c693fcbabf2e24efd" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /script/deploy/goerli/GoerliUpgrade1.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; 5 | 6 | 7 | import "../../../src/contracts/interfaces/IETHPOSDeposit.sol"; 8 | import "../../../src/contracts/interfaces/IBeaconChainOracle.sol"; 9 | 10 | import "../../../src/contracts/core/StrategyManager.sol"; 11 | import "../../../src/contracts/core/Slasher.sol"; 12 | import "../../../src/contracts/core/DelegationManager.sol"; 13 | 14 | import "../../../src/contracts/strategies/StrategyBase.sol"; 15 | 16 | import "../../../src/contracts/pods/EigenPod.sol"; 17 | import "../../../src/contracts/pods/EigenPodManager.sol"; 18 | import "../../../src/contracts/pods/DelayedWithdrawalRouter.sol"; 19 | 20 | 21 | import "forge-std/Script.sol"; 22 | import "forge-std/Test.sol"; 23 | 24 | // # To load the variables in the .env file 25 | // source .env 26 | 27 | // # To deploy and verify our contract 28 | // forge script script/misc/DeployStrategy.s.sol:DeployStrategy --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast -vvvv 29 | 30 | // NOTE: ONLY WORKS ON GOERLI 31 | // CommitHash: eccdfd43bb882d66a68cad8875dde2979e204546 32 | contract GoerliUpgrade1 is Script, Test { 33 | Vm cheats = Vm(HEVM_ADDRESS); 34 | 35 | string public deploymentOutputPath = string(bytes("script/output/M1_deployment_goerli_2023_3_23.json")); 36 | 37 | // EigenLayer Contract 38 | 39 | 40 | function run() external { 41 | // read and log the chainID 42 | uint256 chainId = block.chainid; 43 | emit log_named_uint("You are deploying on ChainID", chainId); 44 | 45 | string memory config_data = vm.readFile(deploymentOutputPath); 46 | IStrategyManager strategyManager = IStrategyManager(stdJson.readAddress(config_data, ".addresses.strategyManager")); 47 | IDelegationManager delegation = IDelegationManager(stdJson.readAddress(config_data, ".addresses.delegation")); 48 | IEigenPodManager eigenPodManager = IEigenPodManager(stdJson.readAddress(config_data, ".addresses.eigenPodManager")); 49 | // IBeacon eigenPodBeacon = IBeacon(stdJson.readAddress(config_data, ".addresses.eigenPodBeacon")); 50 | ISlasher slasher = ISlasher(stdJson.readAddress(config_data, ".addresses.slasher")); 51 | IDelayedWithdrawalRouter delayedWithdrawalRouter = IDelayedWithdrawalRouter(stdJson.readAddress(config_data, ".addresses.delayedWithdrawalRouter")); 52 | 53 | vm.startBroadcast(); 54 | 55 | address strategyManagerImplementation = address( 56 | new StrategyManager( 57 | delegation, 58 | eigenPodManager, 59 | slasher 60 | ) 61 | ); 62 | 63 | address slasherImplementation = address( 64 | new Slasher( 65 | strategyManager, 66 | delegation 67 | ) 68 | ); 69 | 70 | address eigenPodImplementation = address( 71 | new EigenPod( 72 | IETHPOSDeposit(0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b), 73 | delayedWithdrawalRouter, 74 | eigenPodManager, 75 | 32e9, 76 | 1616508000 77 | ) 78 | ); 79 | 80 | vm.stopBroadcast(); 81 | 82 | emit log_named_address("StrategyManagerImplementation", strategyManagerImplementation); 83 | emit log_named_address("SlasherImplementation", slasherImplementation); 84 | emit log_named_address("EigenPodImplementation", eigenPodImplementation); 85 | 86 | // StrategyManagerImplementation: 0x1b8a566357c21b8b7b7c738a6963e2374718ea94 87 | // SlasherImplementation: 0x2f82092969d156da92f0b787525042735fc4774a 88 | // EigenPodImplementation: 0x4dd49853a27e3d4a0557876fe225ffce9b6b5d7a 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /script/deploy/holesky/M2_Deploy_Preprod.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "./M2_Deploy_From_Scratch.s.sol"; 5 | 6 | /** 7 | * @notice Script used for the first deployment of EigenLayer core contracts to Holesky 8 | * forge script script/deploy/holesky/M2_Deploy_Preprod.s.sol --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv 9 | * forge script script/deploy/holesky/M2_Deploy_Preprod.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv 10 | * 11 | * Script for dev environment, exact same as M2_Deploy_From_Scratch.s.sol but with an EOAowner 12 | * instead of multisig addresses for permissions. 13 | * Unused config fields: 14 | * - init_strategy_whitelister 15 | * - multisig_addresses(operations, pauser, executor, community) 16 | */ 17 | contract M2_Deploy_Holesky_Preprod is M2_Deploy_Holesky_From_Scratch { 18 | /// @dev EOAowner is the deployer and owner of the contracts 19 | address EOAowner; 20 | 21 | function run() external virtual override { 22 | _parseInitialDeploymentParams("script/configs/holesky/M2_deploy_preprod.holesky.config.json"); 23 | 24 | // Overwrite multisig to be EOAowner 25 | EOAowner = msg.sender; 26 | executorMultisig = EOAowner; 27 | operationsMultisig = EOAowner; 28 | pauserMultisig = EOAowner; 29 | communityMultisig = EOAowner; 30 | STRATEGY_MANAGER_WHITELISTER = EOAowner; 31 | 32 | // START RECORDING TRANSACTIONS FOR DEPLOYMENT 33 | vm.startBroadcast(); 34 | 35 | emit log_named_address("Deployer and EOAowner Address", EOAowner); 36 | 37 | _deployFromScratch(); 38 | 39 | // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT 40 | vm.stopBroadcast(); 41 | 42 | // Sanity Checks 43 | _verifyContractPointers(); 44 | _verifyImplementations(); 45 | _verifyContractsInitialized({isInitialDeployment: true}); 46 | _verifyInitializationParams(); // override to check contract.owner() is EOAowner instead 47 | 48 | logAndOutputContractAddresses("script/output/holesky/M2_deploy_preprod.holesky.config.json"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /script/output/devnet/M1_MOCK_deployment_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "baseStrategyImplementation": "0x5207CfA0166E8de0FCdFd78B4d17b68587bE306d", 4 | "delayedWithdrawalRouter": "0xD718d5A27a29FF1cD22403426084bA0d479869a0", 5 | "delayedWithdrawalRouterImplementation": "0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4", 6 | "delegation": "0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76", 7 | "delegationImplementation": "0xd21060559c9beb54fC07aFd6151aDf6cFCDDCAeB", 8 | "eigenLayerPauserReg": "0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496", 9 | "eigenLayerProxyAdmin": "0x90193C961A926261B756D1E5bb255e67ff9498A1", 10 | "eigenPodBeacon": "0x416C42991d05b31E9A6dC209e91AD22b79D87Ae6", 11 | "eigenPodImplementation": "0x4f559F30f5eB88D635FDe1548C4267DB8FaB0351", 12 | "eigenPodManager": "0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809", 13 | "eigenPodManagerImplementation": "0x8B71b41D4dBEb2b6821d44692d3fACAAf77480Bb", 14 | "emptyContract": "0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3", 15 | "slasher": "0x62c20Aa1e0272312BC100b4e23B4DC1Ed96dD7D1", 16 | "slasherImplementation": "0x978e3286EB805934215a88694d80b09aDed68D90", 17 | "strategies": { 18 | "WETH": "0x39Af23E00F1e662025aA01b0cEdA19542B78DF99", 19 | "rETH": "0xd6EAF4c146261653EE059077B78ED088Add54309", 20 | "tsETH": "0x970670459734a83899773A0fd45941B5afC1200e", 21 | "wstETH": "0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92" 22 | }, 23 | "strategyManager": "0x50EEf481cae4250d252Ae577A09bF514f224C6C4", 24 | "strategyManagerImplementation": "0x4C52a6277b1B84121b3072C0c92b6Be0b7CC10F1" 25 | }, 26 | "chainInfo": { 27 | "chainId": 31337, 28 | "deploymentBlock": 1 29 | }, 30 | "parameters": { 31 | "communityMultisig": "0x37bAFb55BC02056c5fD891DFa503ee84a97d89bF", 32 | "operationsMultisig": "0x040353E9d057689b77DF275c07FFe1A46b98a4a6", 33 | "executorMultisig": "0x3d9C2c2B40d890ad53E27947402e977155CD2808", 34 | "timelock": "0xA7e72a0564ebf25Fa082Fc27020225edeAF1796E" 35 | } 36 | } -------------------------------------------------------------------------------- /script/output/devnet/M2_from_scratch_deployment_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "baseStrategyImplementation": "0x5207CfA0166E8de0FCdFd78B4d17b68587bE306d", 4 | "blsPublicKeyCompendium": "0x970670459734a83899773A0fd45941B5afC1200e", 5 | "delayedWithdrawalRouter": "0xD718d5A27a29FF1cD22403426084bA0d479869a0", 6 | "delayedWithdrawalRouterImplementation": "0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4", 7 | "delegation": "0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76", 8 | "delegationImplementation": "0xd21060559c9beb54fC07aFd6151aDf6cFCDDCAeB", 9 | "eigenLayerPauserReg": "0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496", 10 | "eigenLayerProxyAdmin": "0x90193C961A926261B756D1E5bb255e67ff9498A1", 11 | "eigenPodBeacon": "0x416C42991d05b31E9A6dC209e91AD22b79D87Ae6", 12 | "eigenPodImplementation": "0x4f559F30f5eB88D635FDe1548C4267DB8FaB0351", 13 | "eigenPodManager": "0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809", 14 | "eigenPodManagerImplementation": "0x8B71b41D4dBEb2b6821d44692d3fACAAf77480Bb", 15 | "emptyContract": "0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3", 16 | "slasher": "0x62c20Aa1e0272312BC100b4e23B4DC1Ed96dD7D1", 17 | "slasherImplementation": "0x978e3286EB805934215a88694d80b09aDed68D90", 18 | "strategies": { 19 | "Coinbase Wrapped Staked ETH": "0x39Af23E00F1e662025aA01b0cEdA19542B78DF99", 20 | "Liquid staked Ether 2.0": "0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92", 21 | "Rocket Pool ETH": "0xd6EAF4c146261653EE059077B78ED088Add54309" 22 | }, 23 | "strategyManager": "0x50EEf481cae4250d252Ae577A09bF514f224C6C4", 24 | "strategyManagerImplementation": "0x4C52a6277b1B84121b3072C0c92b6Be0b7CC10F1" 25 | }, 26 | "chainInfo": { 27 | "chainId": 31337, 28 | "deploymentBlock": 1 29 | }, 30 | "parameters": { 31 | "executorMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 32 | "operationsMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90" 33 | } 34 | } -------------------------------------------------------------------------------- /script/output/goerli/GV2_deployment_2024_6_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "baseStrategyImplementation": "0x81E94e16949AC397d508B5C2557a272faD2F8ebA", 4 | "delayedWithdrawalRouter": "0x89581561f1F98584F88b0d57c2180fb89225388f", 5 | "delayedWithdrawalRouterImplementation": "0xE576731194EC3d8Ba92E7c2B578ea74238772878", 6 | "delegation": "0x1b7b8F6b258f95Cf9596EabB9aa18B62940Eb0a8", 7 | "delegationImplementation": "0x56652542926444Ebce46Fd97aFd80824ed51e58C", 8 | "eigenLayerPauserReg": "0x7cB9c5D6b9702f2f680e4d35cb1fC945D08208F6", 9 | "eigenLayerProxyAdmin": "0x28ceac2ff82B2E00166e46636e2A4818C29902e2", 10 | "eigenPodBeacon": "0x3093F3B560352F896F0e9567019902C9Aff8C9a5", 11 | "eigenPodImplementation": "0x16a0d8aD2A2b12f3f47d0e8F5929F9840e29a426", 12 | "eigenPodManager": "0xa286b84C96aF280a49Fe1F40B9627C2A2827df41", 13 | "eigenPodManagerImplementation": "0xDA9B60D3dC7adD40C0e35c628561Ff71C13a189f", 14 | "emptyContract": "0xa04bf5170D86833294b5c21c712C69C0Fb5735A4", 15 | "slasher": "0xD11d60b669Ecf7bE10329726043B3ac07B380C22", 16 | "slasherImplementation": "0x89C5e6e98f79be658e830Ec66b61ED3EE910D262", 17 | "strategyManager": "0x779d1b5315df083e3F9E94cB495983500bA8E907", 18 | "strategyManagerImplementation": "0x506C21f43e81D9d231d8A13831b42A2a2B5540E4", 19 | "avsDirectory": "0x0AC9694c271eFbA6059e9783769e515E8731f935", 20 | "avsDirectoryImplementation": "0x871cD8f6CFec8b2EB1ac64d58F6D9e1D36a88cb3" 21 | }, 22 | "chainInfo": { 23 | "chainId": 5, 24 | "deploymentBlock": 10497389 25 | }, 26 | "parameters": { 27 | "executorMultisig": "0x3d9C2c2B40d890ad53E27947402e977155CD2808", 28 | "operationsMultisig": "0x040353E9d057689b77DF275c07FFe1A46b98a4a6" 29 | } 30 | } -------------------------------------------------------------------------------- /script/output/goerli/GV2_preprod_deployment_2024_30_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "baseStrategyImplementation": "0xA548BF0106108A0c14779F3f1d8981517b8fA9D0", 4 | "delayedWithdrawalRouter": "0x9572e46797B7A07257314e587061dC46c4dfCE0E", 5 | "delayedWithdrawalRouterImplementation": "0x44a40C60857b4B420Ad3D8b9646FefEBF2D0dB86", 6 | "delegation": "0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332", 7 | "delegationImplementation": "0x934eB3E2b6D5C2E1601B29B7180026D71438F20D", 8 | "eigenLayerPauserReg": "0x94A2679B6A87ADb4e0CabA8E3E40f463C6062DeC", 9 | "eigenLayerProxyAdmin": "0x555573Ff2B3b2731e69eeBAfb40a4EEA7fBaC54A", 10 | "eigenPodBeacon": "0x38cBD4e08eA1840B91dA42fE02B55Abc89083bFB", 11 | "eigenPodImplementation": "0x83cbB48391F428878Bc5DD97C9792a8dbCAa0729", 12 | "eigenPodManager": "0x33e42d539abFe9b387B27b0e467374Bbb76cf925", 13 | "eigenPodManagerImplementation": "0xEEdCC9dB001fB8429721FE21426F51f0Cdd329EC", 14 | "emptyContract": "0xb23633b2240D78502fA308B817C892b2d5778469", 15 | "slasher": "0xF751E8C37ACd3AD5a35D5db03E57dB6F9AD0bDd0", 16 | "slasherImplementation": "0x05c235183e8b9dFb7113Cf92bbDc3f5085324158", 17 | "strategyManager": "0xD309ADd2B269d522112DcEe0dCf0b0f04a09C29e", 18 | "strategyManagerImplementation": "0xb9B69504f1a727E783F4B4248A115D56F4080DF8", 19 | "avsDirectory": "0x47eFB8e38656a805BC6B3b13FA331d34dcDeB374", 20 | "avsDirectoryImplementation": "0x728111B10227F44E5e389e5650725948d1DCcE7A" 21 | }, 22 | "chainInfo": { 23 | "chainId": 5, 24 | "deploymentBlock": 10469472 25 | }, 26 | "parameters": { 27 | "executorMultisig": "0x27977e6E4426A525d055A587d2a0537b4cb376eA", 28 | "operationsMultisig": "0x27977e6E4426A525d055A587d2a0537b4cb376eA" 29 | } 30 | } -------------------------------------------------------------------------------- /script/output/goerli/M1_deployment_goerli_2023_3_23.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "baseStrategyImplementation": "0x2c836f44207A732bC951df98f0cAcE4704432B7E", 4 | "delayedWithdrawalRouter": "0x89581561f1F98584F88b0d57c2180fb89225388f", 5 | "delayedWithdrawalRouterImplementation": "0x6854c0eF4A8dF70a5E832cC3b3b4C5a500063837", 6 | "delegation": "0x1b7b8F6b258f95Cf9596EabB9aa18B62940Eb0a8", 7 | "delegationImplementation": "0xeBea7B291b6631806244Ed0E3d1E585e76BB37fb", 8 | "eigenLayerPauserReg": "0x7cB9c5D6b9702f2f680e4d35cb1fC945D08208F6", 9 | "eigenLayerProxyAdmin": "0x28ceac2ff82B2E00166e46636e2A4818C29902e2", 10 | "eigenPodBeacon": "0x3093F3B560352F896F0e9567019902C9Aff8C9a5", 11 | "eigenPodImplementation": "0x0062645382Af44593bA2E453F51604833277F371", 12 | "eigenPodManager": "0xa286b84C96aF280a49Fe1F40B9627C2A2827df41", 13 | "eigenPodManagerImplementation": "0xad46772384CFec11d140E255e6d240949A194f17", 14 | "emptyContract": "0xa04bf5170D86833294b5c21c712C69C0Fb5735A4", 15 | "slasher": "0xD11d60b669Ecf7bE10329726043B3ac07B380C22", 16 | "slasherImplementation": "0x7dcbcb83f7e744ffa1a94f508419cee8279f469e", 17 | "strategies": { 18 | "WETH": "0x7CA911E83dabf90C90dD3De5411a10F1A6112184", 19 | "rETH": "0x879944A8cB437a5f8061361f82A6d4EED59070b5", 20 | "tsETH": "0xcFA9da720682bC4BCb55116675f16F503093ba13", 21 | "wstETH": "0x13760F50a9d7377e4F20CB8CF9e4c26586c658ff", 22 | "stETH": "0xB613E78E2068d7489bb66419fB1cfa11275d14da" 23 | }, 24 | "strategyManager": "0x779d1b5315df083e3F9E94cB495983500bA8E907", 25 | "strategyManagerImplementation": "0xcf19a2e2d1d83994ad9c89807d4173519aef72a0" 26 | }, 27 | "chainInfo": { 28 | "chainId": 5, 29 | "deploymentBlock": 8705851 30 | }, 31 | "parameters": { 32 | "communityMultisig": "0x37bAFb55BC02056c5fD891DFa503ee84a97d89bF", 33 | "operationsMultisig": "0x040353E9d057689b77DF275c07FFe1A46b98a4a6", 34 | "executorMultisig": "0x3d9C2c2B40d890ad53E27947402e977155CD2808", 35 | "timelock": "0xA7e72a0564ebf25Fa082Fc27020225edeAF1796E" 36 | } 37 | } -------------------------------------------------------------------------------- /script/output/goerli/M2_deployment_data_goerli.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "delayedWithdrawalRouter": "0x89581561f1F98584F88b0d57c2180fb89225388f", 4 | "delegation": "0x1b7b8F6b258f95Cf9596EabB9aa18B62940Eb0a8", 5 | "delegationImplementation": "0x9b7980a32ceCe2Aa936DD2E43AF74af62581A99d", 6 | "eigenPodBeacon": "0x3093F3B560352F896F0e9567019902C9Aff8C9a5", 7 | "eigenPodImplementation": "0x86bf376E0C0c9c6D332E13422f35Aca75C106CcA", 8 | "eigenPodManager": "0xa286b84C96aF280a49Fe1F40B9627C2A2827df41", 9 | "eigenPodManagerImplementation": "0xdD09d95bD25299EDBF4f33d76F84dBc77b0B901b", 10 | "ethPOS": "0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b", 11 | "slasher": "0xD11d60b669Ecf7bE10329726043B3ac07B380C22", 12 | "strategyManager": "0x779d1b5315df083e3F9E94cB495983500bA8E907", 13 | "strategyManagerImplementation": "0x8676bb5f792ED407a237234Fe422aC6ed3540055" 14 | }, 15 | "chainInfo": { 16 | "chainId": 5, 17 | "deploymentBlock": 10002668 18 | } 19 | } -------------------------------------------------------------------------------- /script/output/goerli/M2_preprod_deployment_from_scratch.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "baseStrategyImplementation": "0xA548BF0106108A0c14779F3f1d8981517b8fA9D0", 4 | "blsPublicKeyCompendium": "0x663F1f6A8E4417b9dB3117821068DAD862395aF0", 5 | "delayedWithdrawalRouter": "0x9572e46797B7A07257314e587061dC46c4dfCE0E", 6 | "delayedWithdrawalRouterImplementation": "0xaDd6b52E063bE5CdeF6450F28D9CA038bDAB9A49", 7 | "delegation": "0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332", 8 | "delegationImplementation": "0x679cf51e303827c99e924bea05331101bF90B126", 9 | "eigenLayerPauserReg": "0x94A2679B6A87ADb4e0CabA8E3E40f463C6062DeC", 10 | "eigenLayerProxyAdmin": "0x555573Ff2B3b2731e69eeBAfb40a4EEA7fBaC54A", 11 | "eigenPodBeacon": "0x38cBD4e08eA1840B91dA42fE02B55Abc89083bFB", 12 | "eigenPodImplementation": "0x9CeE917f0f5d4123585A4B12906a8A65cFac1ac8", 13 | "eigenPodManager": "0x33e42d539abFe9b387B27b0e467374Bbb76cf925", 14 | "eigenPodManagerImplementation": "0x6A4855ab9a3924c8169f20a189272FFF3cd00b68", 15 | "emptyContract": "0xb23633b2240D78502fA308B817C892b2d5778469", 16 | "slasher": "0xF751E8C37ACd3AD5a35D5db03E57dB6F9AD0bDd0", 17 | "slasherImplementation": "0xa02171440AfD8d5f09BaAB74Cd48b1401C47F2f9", 18 | "strategies": { 19 | "Liquid staked Ether 2.0": "0xed6DE3f2916d20Cb427fe7255194a05061319FFB", 20 | "Rocket Pool ETH": "0xd421b2a340497545dA68AE53089d99b9Fe0493cD" 21 | }, 22 | "strategyManager": "0xD309ADd2B269d522112DcEe0dCf0b0f04a09C29e", 23 | "strategyManagerImplementation": "0xC10133A329A210f8DEbf597C8eF5907c95D673e9" 24 | }, 25 | "chainInfo": { 26 | "chainId": 5, 27 | "deploymentBlock": 9729808 28 | }, 29 | "parameters": { 30 | "executorMultisig": "0x27977e6E4426A525d055A587d2a0537b4cb376eA", 31 | "operationsMultisig": "0x27977e6E4426A525d055A587d2a0537b4cb376eA" 32 | } 33 | } -------------------------------------------------------------------------------- /script/output/goerli/deployment_output.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "ERC20Mock": "0x7ad75e99869026FE521f34d1239AD633463bA520", 4 | "ERC20MockStrategy": "0x3FF9067f06c7833560d2d669fa58D6b1b788EcF0", 5 | "ERC20MockStrategyImplementation": "0xE0411693E86760840B6Ee90004b0C248ab5c9631", 6 | "delayedWithdrawalRouter": "0x91BbcEd2DB7778c569Fbab34A3957f5ded92bb2d", 7 | "delayedWithdrawalRouterImplementation": "0x007F25A938173F0995daB8e7806aC8b6EbfB7808", 8 | "delegation": "0x1b0870C6a7472ED9Da774b4Ca0Fe1b5fd6B6D61E", 9 | "delegationImplementation": "0x9cCb6f6BC4e7641Cd6d5E7BD7e97f55D9914AaAb", 10 | "eigenLayerPauserReg": "0x18E5227d0E8D8053579d5c1eD6bbd7DD55139454", 11 | "eigenLayerProxyAdmin": "0x81048ca94171C7B97ff0fE590eF67f4B442eD548", 12 | "eigenPodBeacon": "0x4BE52ac49121421A9AF33c476f7f6511Fbf4fCc7", 13 | "eigenPodImplementation": "0xeb873028bA8d079768F11C71b05564D1590238A5", 14 | "eigenPodManager": "0x83622B4e84Daadd0AF1382caE2F8Aa2C67839D9e", 15 | "eigenPodManagerImplementation": "0x009030ab40Db41F9D9336DfED1698D8FFeB6a604", 16 | "emptyContract": "0xbeC65eD486c151202EF673A456e6d8e446726Df6", 17 | "slasher": "0x5B617a19d39Ed8c0754fA31Ef86e6c398Ba1a24E", 18 | "slasherImplementation": "0x8122eD67A26D835349438286FBd0A9cbA6841332", 19 | "strategyManager": "0x5d55B8fDC847c1DF56d1dDd8E278424124199EC3", 20 | "strategyManagerImplementation": "0xC69229bf9E6bb82FfB31fA2fdcEF5431b3a81453" 21 | }, 22 | "chainInfo": { 23 | "chainId": 5, 24 | "deploymentBlock": 9548171 25 | }, 26 | "parameters": { 27 | "alphaMultisig": "0x95C7A3F90e80329C97A6493142Ab7923E15b8083" 28 | } 29 | } -------------------------------------------------------------------------------- /script/output/mainnet/M1_deployment_mainnet_2023_6_9.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "avsDirectory": "0x0000000000000000000000000000000000000000", 4 | "avsDirectoryImplementation": "0x0000000000000000000000000000000000000000", 5 | "baseStrategyImplementation": "0xdfdA04f980bE6A64E3607c95Ca26012Ab9aA46d3", 6 | "beaconOracle": "0x0000000000000000000000000000000000000000", 7 | "delayedWithdrawalRouter": "0x7Fe7E9CC0F274d2435AD5d56D5fa73E47F6A23D8", 8 | "delayedWithdrawalRouterImplementation": "0x44Bcb0E01CD0C5060D4Bb1A07b42580EF983E2AF", 9 | "delegationManager": "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A", 10 | "delegationManagerImplementation": "0xf97E97649Da958d290e84E6D571c32F4b7F475e4", 11 | "eigenLayerPauserReg": "0x0c431C66F4dE941d089625E5B423D00707977060", 12 | "eigenLayerProxyAdmin": "0x8b9566AdA63B64d1E1dcF1418b43fd1433b72444", 13 | "eigenPodBeacon": "0x5a2a4F2F3C18f09179B6703e63D9eDD165909073", 14 | "eigenPodImplementation": "0x5c86e9609fbBc1B754D0FD5a4963Fdf0F5b99dA7", 15 | "eigenPodManager": "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338", 16 | "eigenPodManagerImplementation": "0xEB86a5c40FdE917E6feC440aBbCDc80E3862e111", 17 | "emptyContract": "0x1f96861fEFa1065a5A96F20Deb6D8DC3ff48F7f9", 18 | "slasher": "0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd", 19 | "slasherImplementation": "0xef31c292801f24f16479DD83197F1E6AeBb8d6d8", 20 | "strategies": { 21 | "cbETH": "0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc", 22 | "stETH": "0x93c4b944D05dfe6df7645A86cd2206016c51564D", 23 | "rETH": "0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2" 24 | }, 25 | "strategyManager": "0x858646372CC42E1A627fcE94aa7A7033e7CF075A", 26 | "strategyManagerImplementation": "0x5d25EEf8CfEdaA47d31fE2346726dE1c21e342Fb" 27 | }, 28 | "chainInfo": { 29 | "chainId": 1, 30 | "deploymentBlock": 17445559 31 | }, 32 | "parameters": { 33 | "communityMultisig": "0xFEA47018D632A77bA579846c840d5706705Dc598", 34 | "executorMultisig": "0x369e6F597e22EaB55fFb173C6d9cD234BD699111", 35 | "operationsMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 36 | "pauserMultisig": "0x5050389572f2d220ad927CcbeA0D406831012390", 37 | "timelock": "0xA6Db1A8C5a981d1536266D2a393c5F8dDb210EAF" 38 | }, 39 | "numStrategies": 0 40 | } -------------------------------------------------------------------------------- /script/output/mainnet/M2_mainnet_upgrade.output.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "avsDirectory": "0x135DDa560e946695d6f155dACaFC6f1F25C1F5AF", 4 | "avsDirectoryImplementation": "0xdAbdB3Cd346B7D5F5779b0B614EdE1CC9DcBA5b7", 5 | "baseStrategyImplementation": "0xdfdA04f980bE6A64E3607c95Ca26012Ab9aA46d3", 6 | "beaconOracle": "0x343907185b71aDF0eBa9567538314396aa985442", 7 | "delayedWithdrawalRouter": "0x7Fe7E9CC0F274d2435AD5d56D5fa73E47F6A23D8", 8 | "delayedWithdrawalRouterImplementation": "0x4bB6731B02314d40aBbfFBC4540f508874014226", 9 | "delegationManager": "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A", 10 | "delegationManagerImplementation": "0x1784BE6401339Fc0Fedf7E9379409f5c1BfE9dda", 11 | "eigenLayerPauserReg": "0x0c431C66F4dE941d089625E5B423D00707977060", 12 | "eigenLayerProxyAdmin": "0x8b9566AdA63B64d1E1dcF1418b43fd1433b72444", 13 | "eigenPodBeacon": "0x5a2a4F2F3C18f09179B6703e63D9eDD165909073", 14 | "eigenPodImplementation": "0x8bA40dA60f0827d027F029aCEE62609F0527a255", 15 | "eigenPodManager": "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338", 16 | "eigenPodManagerImplementation": "0xe4297e3DaDBc7D99e26a2954820f514CB50C5762", 17 | "emptyContract": "0x1f96861fEFa1065a5A96F20Deb6D8DC3ff48F7f9", 18 | "slasher": "0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd", 19 | "slasherImplementation": "0xF3234220163a757edf1E11a8a085638D9B236614", 20 | "strategies": "", 21 | "strategyManager": "0x858646372CC42E1A627fcE94aa7A7033e7CF075A", 22 | "strategyManagerImplementation": "0x70f44C13944d49a236E3cD7a94f48f5daB6C619b" 23 | }, 24 | "chainInfo": { 25 | "chainId": 1, 26 | "deploymentBlock": 19492753 27 | }, 28 | "parameters": { 29 | "communityMultisig": "0xFEA47018D632A77bA579846c840d5706705Dc598", 30 | "executorMultisig": "0x369e6F597e22EaB55fFb173C6d9cD234BD699111", 31 | "operationsMultisig": "0xBE1685C81aA44FF9FB319dD389addd9374383e90", 32 | "pauserMultisig": "0x5050389572f2d220ad927CcbeA0D406831012390", 33 | "timelock": "0xA6Db1A8C5a981d1536266D2a393c5F8dDb210EAF" 34 | } 35 | } -------------------------------------------------------------------------------- /script/utils/Multisend.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | /// @title Multi Send Call Only - Allows to batch multiple transactions into one, but only calls 5 | /// @author Stefan George - 6 | /// @author Richard Meissner - 7 | /// @notice The guard logic is not required here as this contract doesn't support nested delegate calls 8 | contract MultiSendCallOnly { 9 | /// @dev Sends multiple transactions and reverts all if one fails. 10 | /// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of 11 | /// operation has to be uint8(0) in this version (=> 1 byte), 12 | /// to as a address (=> 20 bytes), 13 | /// value as a uint256 (=> 32 bytes), 14 | /// data length as a uint256 (=> 32 bytes), 15 | /// data as bytes. 16 | /// see abi.encodePacked for more information on packed encoding 17 | /// @notice The code is for most part the same as the normal MultiSend (to keep compatibility), 18 | /// but reverts if a transaction tries to use a delegatecall. 19 | /// @notice This method is payable as delegatecalls keep the msg.value from the previous call 20 | /// If the calling method (e.g. execTransaction) received ETH this would revert otherwise 21 | function multiSend(bytes memory transactions) public payable { 22 | // solhint-disable-next-line no-inline-assembly 23 | assembly { 24 | let length := mload(transactions) 25 | let i := 0x20 26 | for { 27 | // Pre block is not used in "while mode" 28 | } lt(i, length) { 29 | // Post block is not used in "while mode" 30 | } { 31 | // First byte of the data is the operation. 32 | // We shift by 248 bits (256 - 8 [operation byte]) it right since mload will always load 32 bytes (a word). 33 | // This will also zero out unused data. 34 | let operation := shr(0xf8, mload(add(transactions, i))) 35 | // We offset the load address by 1 byte (operation byte) 36 | // We shift it right by 96 bits (256 - 160 [20 address bytes]) to right-align the data and zero out unused data. 37 | let to := shr(0x60, mload(add(transactions, add(i, 0x01)))) 38 | // We offset the load address by 21 byte (operation byte + 20 address bytes) 39 | let value := mload(add(transactions, add(i, 0x15))) 40 | // We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes) 41 | let dataLength := mload(add(transactions, add(i, 0x35))) 42 | // We offset the load address by 85 byte (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes) 43 | let data := add(transactions, add(i, 0x55)) 44 | let success := 0 45 | switch operation 46 | case 0 { 47 | success := call(gas(), to, value, data, dataLength, 0, 0) 48 | } 49 | // This version does not allow delegatecalls 50 | case 1 { 51 | revert(0, 0) 52 | } 53 | if eq(success, 0) { 54 | revert(0, 0) 55 | } 56 | // Next entry starts at 85 byte + data length 57 | i := add(i, add(0x55, dataLength)) 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /script/utils/TimelockEncoding.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "forge-std/Script.sol"; 5 | import "forge-std/Test.sol"; 6 | 7 | import "./TxEncodingInterfaces.sol"; 8 | 9 | contract TimelockEncoding is Test { 10 | // CALLDATA FOR CALL FROM TIMELOCK TO EXECUTOR MULTISIG 11 | uint256 safeTxGas = 0; 12 | uint256 baseGas = 0; 13 | uint256 gasPrice = 0; 14 | address gasToken = address(uint160(0)); 15 | address payable refundReceiver = payable(address(uint160(0))); 16 | 17 | // CALDATA FOR CALL TO TIMELOCK 18 | uint256 timelockValue = 0; 19 | // empty string, just encode all the data in 'timelockData' 20 | string timelockSignature; 21 | 22 | // appropriate address on mainnet, Holesky, and many other chains 23 | address multiSendCallOnly = 0x40A2aCCbd92BCA938b02010E17A5b8929b49130D; 24 | 25 | function encodeForTimelock( 26 | address to, 27 | uint256 value, 28 | bytes memory data, 29 | uint256 timelockEta 30 | ) public returns (bytes memory calldata_to_timelock_queuing_action, bytes memory calldata_to_timelock_executing_action) { 31 | calldata_to_timelock_queuing_action = abi.encodeWithSelector(ITimelock.queueTransaction.selector, 32 | to, 33 | value, 34 | timelockSignature, 35 | data, 36 | timelockEta 37 | ); 38 | 39 | emit log_named_bytes("calldata_to_timelock_queuing_action", calldata_to_timelock_queuing_action); 40 | 41 | calldata_to_timelock_executing_action = abi.encodeWithSelector(ITimelock.executeTransaction.selector, 42 | to, 43 | value, 44 | timelockSignature, 45 | data, 46 | timelockEta 47 | ); 48 | 49 | emit log_named_bytes("calldata_to_timelock_executing_action", calldata_to_timelock_executing_action); 50 | 51 | return (calldata_to_timelock_queuing_action, calldata_to_timelock_executing_action); 52 | } 53 | 54 | function encodeForExecutor( 55 | address from, 56 | address to, 57 | uint256 value, 58 | bytes memory data, 59 | ISafe.Operation operation 60 | ) public returns (bytes memory) { 61 | // encode the "signature" required by the Safe 62 | bytes1 v = bytes1(uint8(1)); 63 | bytes32 r = bytes32(uint256(uint160(from))); 64 | bytes32 s; 65 | bytes memory sig = abi.encodePacked(r,s,v); 66 | emit log_named_bytes("sig", sig); 67 | 68 | bytes memory final_calldata_to_executor_multisig = abi.encodeWithSelector(ISafe.execTransaction.selector, 69 | to, 70 | value, 71 | data, 72 | operation, 73 | safeTxGas, 74 | baseGas, 75 | gasPrice, 76 | gasToken, 77 | refundReceiver, 78 | sig 79 | ); 80 | 81 | emit log_named_bytes("final_calldata_to_executor_multisig", final_calldata_to_executor_multisig); 82 | 83 | return final_calldata_to_executor_multisig; 84 | } 85 | 86 | struct Tx { 87 | address to; 88 | uint256 value; 89 | bytes data; 90 | } 91 | 92 | function encodeMultisendTxs(Tx[] memory txs) public pure returns (bytes memory) { 93 | bytes memory ret = new bytes(0); 94 | for (uint256 i = 0; i < txs.length; i++) { 95 | ret = abi.encodePacked( 96 | ret, 97 | abi.encodePacked( 98 | uint8(0), 99 | txs[i].to, 100 | txs[i].value, 101 | uint256(txs[i].data.length), 102 | txs[i].data 103 | ) 104 | ); 105 | } 106 | return ret; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /script/utils/TxEncodingInterfaces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL AND BSD 3-Clause 2 | pragma solidity >=0.5.0; 3 | 4 | // based on https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/GnosisSafe.sol 5 | interface ISafe { 6 | function execTransaction( 7 | address to, 8 | uint256 value, 9 | bytes calldata data, 10 | uint8 operation, 11 | uint256 safeTxGas, 12 | uint256 baseGas, 13 | uint256 gasPrice, 14 | address gasToken, 15 | address payable refundReceiver, 16 | bytes memory signatures 17 | ) external; 18 | 19 | enum Operation {Call, DelegateCall} 20 | } 21 | 22 | // based on https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol 23 | interface ITimelock { 24 | function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) external returns (bytes32); 25 | function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) external payable returns (bytes memory); 26 | function queuedTransactions(bytes32) external view returns (bool); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /script/utils/validateStorage/README.md: -------------------------------------------------------------------------------- 1 | # Storage Validation Scripts 2 | This script uses cast and forge inspect to get the layouts of the local and on-chain contract. Your `ETHERSCAN_API_KEY` must be set as an environment variable. The storage layouts are saved into csv files and passed into the typescript helper validateStorage.ts, which takes paths to two csv layouts and validates the storage slots. 3 | 4 | ## Run validation 5 | To validate the storage of an upgradeable deployed contract against a local one, run the script: 6 | ```bash 7 | bash script/upgrade/validateUpgrade.sh -n -c -a 8 | ``` 9 | 10 | The supported networks are `goerli` and `mainnet`. The supported contracts are `strategyManager`, `delegation`, `eigenPod`, `eigenPodManager`, and `slasher`. 11 | 12 | The above script generates two csv files, `localLayout.csv` and `onChainLayout.csv`. To keep these csv files after validating storage, add a `-k` flag to the above command 13 | 14 | Additionally, one can validate the storage of two csv files outputted by the `forge inspect` command by running 15 | 16 | ```js 17 | npx ts-node script/upgrade/validateStorage.ts --old --new --keep 18 | ``` 19 | 20 | ## Limitations 21 | Storage slot validation is NOT comprehensive, and errs on the side of caution. We recommend using this script as a tool along with manual storage slot verification. The validation is opinionated on storage for each contract consuming 50 slots and gaps being sized accordingly. 22 | 23 | The script does not validate legal type changes (ie. from bool to uint8) and errors out if the types of slots have updated, including having different struct names. A manual check will need to be done to validate this conversion. In addition, the script does not support non-contiguous gaps. -------------------------------------------------------------------------------- /script/utils/validateStorage/validateUpgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .env 4 | 5 | # Parse command-line arguments using getopt 6 | while getopts ":n:c:a:k:" opt; do 7 | case $opt in 8 | n) NETWORK="$OPTARG";; 9 | c) CONTRACT="$OPTARG";; 10 | a) ADDRESS="$OPTARG";; 11 | \?) echo "Invalid option -$OPTARG" >&2; exit 1;; 12 | esac 13 | done 14 | 15 | # Validate that network and contract inputs are provided 16 | if [ -z "$NETWORK" ] || [ -z "$CONTRACT" ] || [ -z "$ADDRESS" ]; then 17 | echo "Usage: $0 -n -c -a
-k" 18 | exit 1 19 | fi 20 | 21 | # Validate the network input 22 | if [ "$NETWORK" != "mainnet" ] && [ "$NETWORK" != "goerli" ]; then 23 | echo "Invalid network. Use 'mainnet' or 'goerli'." 24 | exit 1 25 | fi 26 | 27 | # Get local path for contract & validate contract input 28 | case $CONTRACT in 29 | "strategyManager") CONTRACT_PATH="src/contracts/core/StrategyManager.sol:StrategyManager";; 30 | "delegation") CONTRACT_PATH="src/contracts/core/DelegationManager.sol:DelegationManager";; 31 | "eigenPodManager") CONTRACT_PATH="src/contracts/pods/EigenPodManager.sol:EigenPodManager";; 32 | "eigenPod") CONTRACT_PATH="src/contracts/pods/EigenPod.sol:EigenPod";; 33 | "slasher") CONTRACT_PATH="src/contracts/core/Slasher.sol:Slasher";; 34 | *) 35 | echo "Invalid contract name." 36 | exit 1 37 | ;; 38 | esac 39 | 40 | # Set RPC 41 | if [ "$NETWORK" == "goerli" ]; then 42 | RPC_URL="$RPC_GOERLI" 43 | else 44 | RPC_URL="$RPC_MAINNET" 45 | fi 46 | 47 | # Print the selected network and contract 48 | echo "Checking storage layouts for contract on: $NETWORK" 49 | echo "Contract to validate upgrade: $CONTRACT" 50 | 51 | # Get storage layout for on-chain contract 52 | echo "Retrieving on-chain storage layout for $ADDRESS" 53 | command="cast storage $ADDRESS --rpc-url $RPC_URL --etherscan-api-key $ETHERSCAN_API_KEY" 54 | eval "$command > /dev/null 2>&1" # precompile contracts so onChainLayout.csv isn't filled with warnings 55 | output=$(eval $command) 56 | echo "$output" | tail -n +2 > onChainLayout.csv 57 | echo "On-chain storage saved to onChainLayout.csv" 58 | 59 | # Get storage layout for local contract 60 | echo "Retrieving local storage layout for $CONTRACT at $CONTRACT_PATH" 61 | command="forge inspect $CONTRACT_PATH storage --pretty" 62 | output=$(eval $command) 63 | echo "$output" | tail -n +1 > localLayout.csv 64 | echo "Local storage saved to localLayout.csv" 65 | 66 | # Compare the two storage layouts via typescript script 67 | echo "Comparing storage layouts..." 68 | 69 | # Add -k operator if present 70 | if [ ! -k "$1" ]; then 71 | echo "Keeping old storage layout files" 72 | eval "npx ts-node script/utils/validateStorage/validateStorage.ts --old onChainLayout.csv --new localLayout.csv --keep" 73 | else 74 | eval "npx ts-node script/utils/validateStorage/validateStorage.ts --old onChainLayout.csv --new localLayout.csv" 75 | fi -------------------------------------------------------------------------------- /script/whitelist/ERC20PresetMinterPauser.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetMinterPauser.sol) 3 | 4 | pragma solidity ^0.8.0; 5 | 6 | import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; 7 | import "@openzeppelin/contracts/utils/Context.sol"; 8 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 9 | 10 | 11 | /** 12 | * @dev {ERC20} token, including: 13 | * 14 | * - ability for holders to burn (destroy) their tokens 15 | * - a minter role that allows for token minting (creation) 16 | * - a pauser role that allows to stop all token transfers 17 | * 18 | * This contract uses {AccessControl} to lock permissioned functions using the 19 | * different roles - head to its documentation for details. 20 | * 21 | * The account that deploys the contract will be granted the minter and pauser 22 | * roles, as well as the default admin role, which will let it grant both minter 23 | * and pauser roles to other accounts. 24 | * 25 | * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ 26 | */ 27 | contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable { 28 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); 29 | bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); 30 | 31 | /** 32 | * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the 33 | * account that deploys the contract. 34 | * 35 | * See {ERC20-constructor}. 36 | */ 37 | constructor(string memory name, string memory symbol) ERC20(name, symbol) { 38 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); 39 | 40 | _setupRole(MINTER_ROLE, _msgSender()); 41 | _setupRole(PAUSER_ROLE, _msgSender()); 42 | } 43 | 44 | /** 45 | * @dev Creates `amount` new tokens for `to`. 46 | * 47 | * See {ERC20-_mint}. 48 | * 49 | * Requirements: 50 | * 51 | * - the caller must have the `MINTER_ROLE`. 52 | */ 53 | function mint(address to, uint256 amount) public virtual { 54 | require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint"); 55 | _mint(to, amount); 56 | } 57 | 58 | /** 59 | * @dev Pauses all token transfers. 60 | * 61 | * See {ERC20Pausable} and {Pausable-_pause}. 62 | * 63 | * Requirements: 64 | * 65 | * - the caller must have the `PAUSER_ROLE`. 66 | */ 67 | function pause() public virtual { 68 | require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause"); 69 | 70 | } 71 | 72 | /** 73 | * @dev Unpauses all token transfers. 74 | * 75 | * See {ERC20Pausable} and {Pausable-_unpause}. 76 | * 77 | * Requirements: 78 | * 79 | * - the caller must have the `PAUSER_ROLE`. 80 | */ 81 | function unpause() public virtual { 82 | require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause"); 83 | } 84 | 85 | function _beforeTokenTransfer( 86 | address from, 87 | address to, 88 | uint256 amount 89 | ) internal virtual override(ERC20) { 90 | super._beforeTokenTransfer(from, to, amount); 91 | } 92 | } -------------------------------------------------------------------------------- /script/whitelist/Staker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../src/contracts/interfaces/IStrategyManager.sol"; 5 | import "../../src/contracts/interfaces/IStrategy.sol"; 6 | import "../../src/contracts/interfaces/IDelegationManager.sol"; 7 | import "../../src/contracts/interfaces/ISignatureUtils.sol"; 8 | 9 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 10 | import "@openzeppelin/contracts/access/Ownable.sol"; 11 | import "forge-std/Test.sol"; 12 | 13 | contract Staker is Ownable { 14 | 15 | constructor( 16 | IStrategy strategy, 17 | IStrategyManager strategyManager, 18 | IDelegationManager delegation, 19 | IERC20 token, 20 | uint256 amount, 21 | address operator 22 | ) Ownable() { 23 | token.approve(address(strategyManager), type(uint256).max); 24 | strategyManager.depositIntoStrategy(strategy, token, amount); 25 | ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry; 26 | delegation.delegateTo(operator, signatureWithExpiry, bytes32(0)); 27 | } 28 | 29 | function callAddress(address implementation, bytes memory data) external onlyOwner returns(bytes memory) { 30 | uint256 length = data.length; 31 | bytes memory returndata; 32 | assembly{ 33 | let result := call( 34 | gas(), 35 | implementation, 36 | callvalue(), 37 | add(data, 32), 38 | length, 39 | 0, 40 | 0 41 | ) 42 | mstore(returndata, returndatasize()) 43 | returndatacopy(add(returndata, 32), 0, returndatasize()) 44 | } 45 | 46 | 47 | return returndata; 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /script/whitelist/delegationFaucet/DelegationFaucetStaker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "src/contracts/interfaces/IStrategyManager.sol"; 5 | 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | import "forge-std/Test.sol"; 9 | 10 | contract DelegationFaucetStaker is Ownable { 11 | constructor(IStrategyManager strategyManager, IERC20 token) Ownable() { 12 | token.approve(address(strategyManager), type(uint256).max); 13 | } 14 | 15 | function callAddress(address implementation, bytes memory data) external onlyOwner returns (bytes memory) { 16 | uint256 length = data.length; 17 | bytes memory returndata; 18 | assembly { 19 | let result := call(gas(), implementation, callvalue(), add(data, 32), length, 0, 0) 20 | mstore(returndata, returndatasize()) 21 | returndatacopy(add(returndata, 32), 0, returndatasize()) 22 | } 23 | 24 | return returndata; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /script/whitelist/delegationFaucet/DeployDelegationFaucet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "src/contracts/interfaces/IDelegationManager.sol"; 5 | import "src/contracts/interfaces/IStrategyManager.sol"; 6 | import "src/contracts/strategies/StrategyBase.sol"; 7 | import "src/contracts/permissions/PauserRegistry.sol"; 8 | 9 | import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; 10 | 11 | import "./DelegationFaucet.sol"; 12 | 13 | import "forge-std/Script.sol"; 14 | import "forge-std/StdJson.sol"; 15 | 16 | /** 17 | * @notice Deploys the following contracts: 18 | * - ERC20 Dummy token for testing 19 | * - StrategyBase to be added to the StrategyManager whitelist 20 | * - DelegationFaucet contract 21 | */ 22 | contract DeployDelegationFaucet is Script, DSTest { 23 | // EigenLayer contracts 24 | ProxyAdmin public eigenLayerProxyAdmin; 25 | PauserRegistry public eigenLayerPauserReg; 26 | IDelegationManager public delegation; 27 | IStrategyManager public strategyManager; 28 | 29 | DelegationFaucet public delegationFaucet; 30 | 31 | // M2 testing/mock contracts 32 | ERC20PresetMinterPauser public stakeToken; 33 | StrategyBase public stakeTokenStrat; 34 | StrategyBase public baseStrategyImplementation; 35 | 36 | address eigenLayerProxyAdminAddress; 37 | address eigenLayerPauserRegAddress; 38 | address delegationAddress; 39 | address strategyManagerAddress; 40 | address operationsMultisig; 41 | address executorMultisig; 42 | 43 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); 44 | 45 | function run() external { 46 | string memory goerliDeploymentConfig = vm.readFile("script/output/M1_deployment_goerli_2023_3_23.json"); 47 | _setAddresses(goerliDeploymentConfig); 48 | 49 | vm.startBroadcast(); 50 | // Deploy ERC20 stakeToken 51 | stakeToken = new ERC20PresetMinterPauser("StakeToken", "STK"); 52 | 53 | // Deploy StrategyBase for stakeToken & whitelist it 54 | baseStrategyImplementation = new StrategyBase(strategyManager); 55 | stakeTokenStrat = StrategyBase( 56 | address( 57 | new TransparentUpgradeableProxy( 58 | address(baseStrategyImplementation), 59 | eigenLayerProxyAdminAddress, 60 | abi.encodeWithSelector(StrategyBase.initialize.selector, stakeToken, eigenLayerPauserRegAddress) 61 | ) 62 | ) 63 | ); 64 | 65 | // Needs to be strategyManager.strategyWhitelister() to add STK strategy 66 | // IStrategy[] memory _strategy = new IStrategy[](1); 67 | // _strategy[0] = stakeTokenStrat; 68 | // strategyManager.addStrategiesToDepositWhitelist(_strategy); 69 | 70 | // Deploy DelegationFaucet, grant it admin/mint/pauser roles, etc. 71 | delegationFaucet = new DelegationFaucet(strategyManager, delegation, stakeToken, stakeTokenStrat); 72 | stakeToken.grantRole(MINTER_ROLE, address(delegationFaucet)); 73 | vm.stopBroadcast(); 74 | } 75 | 76 | function _setAddresses(string memory config) internal { 77 | eigenLayerProxyAdminAddress = stdJson.readAddress(config, ".addresses.eigenLayerProxyAdmin"); 78 | eigenLayerPauserRegAddress = stdJson.readAddress(config, ".addresses.eigenLayerPauserReg"); 79 | delegationAddress = stdJson.readAddress(config, ".addresses.delegation"); 80 | strategyManagerAddress = stdJson.readAddress(config, ".addresses.strategyManager"); 81 | operationsMultisig = stdJson.readAddress(config, ".parameters.operationsMultisig"); 82 | executorMultisig = stdJson.readAddress(config, ".parameters.executorMultisig"); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "detectors_to_exclude": "assembly,solc-version,naming-convention,incorrect-equality,uninitialized-local,timestamp,low-level-calls,unimplemented-functions,too-many-digits,similar-names,calls-loop,arbitrary-send-eth,reentrancy-no-eth,reentrancy-benign,reentrancy-events,unused-state,incorrect-shift-in-assembly,dead-code", 3 | "filter_paths": "lib/|test/|mocks/|BytesLib|script/", 4 | "solc_remaps": [ 5 | "forge-std/=lib/forge-std/src/", 6 | "@openzeppelin/=lib/openzeppelin-contracts/", 7 | "@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable/", 8 | "ds-test/=lib/ds-test/src/" 9 | ], 10 | "compile_force_framework": "foundry" 11 | } -------------------------------------------------------------------------------- /source-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if yq is installed 4 | if ! command -v yq &> /dev/null 5 | then 6 | echo "yq is not installed. Please install it and try again." 7 | return 1 8 | fi 9 | 10 | # Check for arguments 11 | if [ "$#" -ne 1 ]; then 12 | echo "Usage: $0 [goerli|local]" 13 | return 1 14 | fi 15 | 16 | # Read the YAML file 17 | CONFIG_FILE="config.yml" 18 | 19 | case $1 in 20 | goerli) 21 | CHAIN_ID=$(yq e '.goerli.CHAIN_ID' $CONFIG_FILE) 22 | EXECUTOR_MULTISIG=$(yq e '.goerli.EXECUTOR_MULTISIG' $CONFIG_FILE) 23 | FOUNDRY_FUZZ_RUNS=$(yq e '.goerli.FOUNDRY_FUZZ_RUNS' $CONFIG_FILE) 24 | ;; 25 | local) 26 | CHAIN_ID=$(yq e '.local.CHAIN_ID' $CONFIG_FILE) 27 | FOUNDRY_FUZZ_RUNS=$(yq e '.local.FOUNDRY_FUZZ_RUNS' $CONFIG_FILE) 28 | ;; 29 | *) 30 | echo "Invalid argument. Usage: $0 [goerli|local]" 31 | return 1 32 | ;; 33 | esac 34 | 35 | # Export environment variables 36 | export CHAIN_ID=$CHAIN_ID 37 | export EXECUTOR_MULTISIG=$EXECUTOR_MULTISIG 38 | export FOUNDRY_FUZZ_RUNS=$FOUNDRY_FUZZ_RUNS 39 | 40 | # Print environment variables 41 | echo "Environment variables set:" 42 | echo "CHAIN_ID: $CHAIN_ID" 43 | echo "EXECUTOR_MULTISIG: $EXECUTOR_MULTISIG" 44 | echo "FOUNDRY_FUZZ_RUNS: $FOUNDRY_FUZZ_RUNS" -------------------------------------------------------------------------------- /src/contracts/core/AVSDirectoryStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../interfaces/IAVSDirectory.sol"; 5 | import "../interfaces/IStrategyManager.sol"; 6 | import "../interfaces/IDelegationManager.sol"; 7 | import "../interfaces/ISlasher.sol"; 8 | import "../interfaces/IEigenPodManager.sol"; 9 | 10 | abstract contract AVSDirectoryStorage is IAVSDirectory { 11 | /// @notice The EIP-712 typehash for the contract's domain 12 | bytes32 public constant DOMAIN_TYPEHASH = 13 | keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); 14 | 15 | /// @notice The EIP-712 typehash for the `Registration` struct used by the contract 16 | bytes32 public constant OPERATOR_AVS_REGISTRATION_TYPEHASH = 17 | keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)"); 18 | 19 | /// @notice The DelegationManager contract for EigenLayer 20 | IDelegationManager public immutable delegation; 21 | 22 | /** 23 | * @notice Original EIP-712 Domain separator for this contract. 24 | * @dev The domain separator may change in the event of a fork that modifies the ChainID. 25 | * Use the getter function `domainSeparator` to get the current domain separator for this contract. 26 | */ 27 | bytes32 internal _DOMAIN_SEPARATOR; 28 | 29 | /// @notice Mapping: AVS => operator => enum of operator status to the AVS 30 | mapping(address => mapping(address => OperatorAVSRegistrationStatus)) public avsOperatorStatus; 31 | 32 | /// @notice Mapping: operator => 32-byte salt => whether or not the salt has already been used by the operator. 33 | /// @dev Salt is used in the `registerOperatorToAVS` function. 34 | mapping(address => mapping(bytes32 => bool)) public operatorSaltIsSpent; 35 | 36 | constructor(IDelegationManager _delegation) { 37 | delegation = _delegation; 38 | } 39 | 40 | /** 41 | * @dev This empty reserved space is put in place to allow future versions to add new 42 | * variables without shifting down storage in the inheritance chain. 43 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 44 | */ 45 | uint256[47] private __gap; 46 | } 47 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IAVSDirectory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | import "./ISignatureUtils.sol"; 5 | 6 | interface IAVSDirectory is ISignatureUtils { 7 | /// @notice Enum representing the status of an operator's registration with an AVS 8 | enum OperatorAVSRegistrationStatus { 9 | UNREGISTERED, // Operator not registered to AVS 10 | REGISTERED // Operator registered to AVS 11 | } 12 | 13 | /** 14 | * @notice Emitted when @param avs indicates that they are updating their MetadataURI string 15 | * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing 16 | */ 17 | event AVSMetadataURIUpdated(address indexed avs, string metadataURI); 18 | 19 | /// @notice Emitted when an operator's registration status for an AVS is updated 20 | event OperatorAVSRegistrationStatusUpdated(address indexed operator, address indexed avs, OperatorAVSRegistrationStatus status); 21 | 22 | /** 23 | * @notice Called by an avs to register an operator with the avs. 24 | * @param operator The address of the operator to register. 25 | * @param operatorSignature The signature, salt, and expiry of the operator's signature. 26 | */ 27 | function registerOperatorToAVS( 28 | address operator, 29 | ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature 30 | ) external; 31 | 32 | /** 33 | * @notice Called by an avs to deregister an operator with the avs. 34 | * @param operator The address of the operator to deregister. 35 | */ 36 | function deregisterOperatorFromAVS(address operator) external; 37 | 38 | /** 39 | * @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated. 40 | * @param metadataURI The URI for metadata associated with an AVS 41 | * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `AVSMetadataURIUpdated` event 42 | */ 43 | function updateAVSMetadataURI(string calldata metadataURI) external; 44 | 45 | /** 46 | * @notice Returns whether or not the salt has already been used by the operator. 47 | * @dev Salts is used in the `registerOperatorToAVS` function. 48 | */ 49 | function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool); 50 | 51 | /** 52 | * @notice Calculates the digest hash to be signed by an operator to register with an AVS 53 | * @param operator The account registering as an operator 54 | * @param avs The AVS the operator is registering to 55 | * @param salt A unique and single use value associated with the approver signature. 56 | * @param expiry Time after which the approver's signature becomes invalid 57 | */ 58 | function calculateOperatorAVSRegistrationDigestHash( 59 | address operator, 60 | address avs, 61 | bytes32 salt, 62 | uint256 expiry 63 | ) external view returns (bytes32); 64 | 65 | /// @notice The EIP-712 typehash for the Registration struct used by the contract 66 | function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32); 67 | } 68 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IBeaconChainOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | /** 5 | * @title Interface for the BeaconStateOracle contract. 6 | * @author Layr Labs, Inc. 7 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 8 | */ 9 | interface IBeaconChainOracle { 10 | /// @notice The block number to state root mapping. 11 | function timestampToBlockRoot(uint256 timestamp) external view returns (bytes32); 12 | } 13 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IDelegationFaucet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | import "src/contracts/interfaces/IStrategyManager.sol"; 5 | import "src/contracts/interfaces/IDelegationManager.sol"; 6 | 7 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | 9 | interface IDelegationFaucet { 10 | function mintDepositAndDelegate( 11 | address _operator, 12 | IDelegationManager.SignatureWithExpiry memory approverSignatureAndExpiry, 13 | bytes32 approverSalt, 14 | uint256 _depositAmount 15 | ) external; 16 | 17 | function getStaker(address operator) external returns (address); 18 | 19 | function depositIntoStrategy( 20 | address staker, 21 | IStrategy strategy, 22 | IERC20 token, 23 | uint256 amount 24 | ) external returns (bytes memory); 25 | 26 | function queueWithdrawal( 27 | address staker, 28 | IDelegationManager.QueuedWithdrawalParams[] calldata queuedWithdrawalParams 29 | ) external returns (bytes memory); 30 | 31 | function completeQueuedWithdrawal( 32 | address staker, 33 | IDelegationManager.Withdrawal calldata queuedWithdrawal, 34 | IERC20[] calldata tokens, 35 | uint256 middlewareTimesIndex, 36 | bool receiveAsTokens 37 | ) external returns (bytes memory); 38 | 39 | function transfer(address staker, address token, address to, uint256 amount) external returns (bytes memory); 40 | 41 | function callAddress(address to, bytes memory data) external payable returns (bytes memory); 42 | } 43 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IETHPOSDeposit.sol: -------------------------------------------------------------------------------- 1 | // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━ 2 | // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓ 3 | // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛ 4 | // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━ 5 | // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓ 6 | // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛ 7 | // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8 | // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9 | 10 | // SPDX-License-Identifier: CC0-1.0 11 | 12 | pragma solidity >=0.5.0; 13 | 14 | // This interface is designed to be compatible with the Vyper version. 15 | /// @notice This is the Ethereum 2.0 deposit contract interface. 16 | /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs 17 | interface IETHPOSDeposit { 18 | /// @notice A processed deposit event. 19 | event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index); 20 | 21 | /// @notice Submit a Phase 0 DepositData object. 22 | /// @param pubkey A BLS12-381 public key. 23 | /// @param withdrawal_credentials Commitment to a public key for withdrawals. 24 | /// @param signature A BLS12-381 signature. 25 | /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object. 26 | /// Used as a protection against malformed input. 27 | function deposit( 28 | bytes calldata pubkey, 29 | bytes calldata withdrawal_credentials, 30 | bytes calldata signature, 31 | bytes32 deposit_data_root 32 | ) external payable; 33 | 34 | /// @notice Query the current deposit root hash. 35 | /// @return The deposit root hash. 36 | function get_deposit_root() external view returns (bytes32); 37 | 38 | /// @notice Query the current deposit count. 39 | /// @return The deposit count encoded as a little endian 64-bit number. 40 | function get_deposit_count() external view returns (bytes memory); 41 | } 42 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IPauserRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | /** 5 | * @title Interface for the `PauserRegistry` contract. 6 | * @author Layr Labs, Inc. 7 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 8 | */ 9 | interface IPauserRegistry { 10 | event PauserStatusChanged(address pauser, bool canPause); 11 | 12 | event UnpauserChanged(address previousUnpauser, address newUnpauser); 13 | 14 | /// @notice Mapping of addresses to whether they hold the pauser role. 15 | function isPauser(address pauser) external view returns (bool); 16 | 17 | /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses. 18 | function unpauser() external view returns (address); 19 | } 20 | -------------------------------------------------------------------------------- /src/contracts/interfaces/ISignatureUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | /** 5 | * @title The interface for common signature utilities. 6 | * @author Layr Labs, Inc. 7 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 8 | */ 9 | interface ISignatureUtils { 10 | // @notice Struct that bundles together a signature and an expiration time for the signature. Used primarily for stack management. 11 | struct SignatureWithExpiry { 12 | // the signature itself, formatted as a single bytes object 13 | bytes signature; 14 | // the expiration timestamp (UTC) of the signature 15 | uint256 expiry; 16 | } 17 | 18 | // @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management. 19 | struct SignatureWithSaltAndExpiry { 20 | // the signature itself, formatted as a single bytes object 21 | bytes signature; 22 | // the salt used to generate the signature 23 | bytes32 salt; 24 | // the expiration timestamp (UTC) of the signature 25 | uint256 expiry; 26 | } 27 | } -------------------------------------------------------------------------------- /src/contracts/interfaces/ISocketUpdater.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | /** 5 | * @title Interface for an `ISocketUpdater` where operators can update their sockets. 6 | * @author Layr Labs, Inc. 7 | */ 8 | interface ISocketUpdater { 9 | // EVENTS 10 | 11 | event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); 12 | 13 | // FUNCTIONS 14 | 15 | /** 16 | * @notice Updates the socket of the msg.sender given they are a registered operator 17 | * @param socket is the new socket of the operator 18 | */ 19 | function updateSocket(string memory socket) external; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IWhitelister.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | import "../../contracts/interfaces/IStrategyManager.sol"; 5 | import "../../contracts/interfaces/IStrategy.sol"; 6 | import "../../contracts/interfaces/IDelegationManager.sol"; 7 | import "../../../script/whitelist/Staker.sol"; 8 | 9 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 10 | import "@openzeppelin/contracts/access/Ownable.sol"; 11 | import "@openzeppelin/contracts/utils/Create2.sol"; 12 | 13 | interface IWhitelister { 14 | function whitelist(address operator) external; 15 | 16 | function getStaker(address operator) external returns (address); 17 | 18 | function depositIntoStrategy( 19 | address staker, 20 | IStrategy strategy, 21 | IERC20 token, 22 | uint256 amount 23 | ) external returns (bytes memory); 24 | 25 | function queueWithdrawal( 26 | address staker, 27 | IDelegationManager.QueuedWithdrawalParams[] calldata queuedWithdrawalParams 28 | ) external returns (bytes memory); 29 | 30 | function completeQueuedWithdrawal( 31 | address staker, 32 | IDelegationManager.Withdrawal calldata queuedWithdrawal, 33 | IERC20[] calldata tokens, 34 | uint256 middlewareTimesIndex, 35 | bool receiveAsTokens 36 | ) external returns (bytes memory); 37 | 38 | function transfer(address staker, address token, address to, uint256 amount) external returns (bytes memory); 39 | 40 | function callAddress(address to, bytes memory data) external payable returns (bytes memory); 41 | } 42 | -------------------------------------------------------------------------------- /src/contracts/libraries/EIP1271SignatureUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/interfaces/IERC1271.sol"; 5 | import "@openzeppelin/contracts/utils/Address.sol"; 6 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 7 | 8 | /** 9 | * @title Library of utilities for making EIP1271-compliant signature checks. 10 | * @author Layr Labs, Inc. 11 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 12 | */ 13 | library EIP1271SignatureUtils { 14 | // bytes4(keccak256("isValidSignature(bytes32,bytes)") 15 | bytes4 internal constant EIP1271_MAGICVALUE = 0x1626ba7e; 16 | 17 | /** 18 | * @notice Checks @param signature is a valid signature of @param digestHash from @param signer. 19 | * If the `signer` contains no code -- i.e. it is not (yet, at least) a contract address, then checks using standard ECDSA logic 20 | * Otherwise, passes on the signature to the signer to verify the signature and checks that it returns the `EIP1271_MAGICVALUE`. 21 | */ 22 | function checkSignature_EIP1271(address signer, bytes32 digestHash, bytes memory signature) internal view { 23 | /** 24 | * check validity of signature: 25 | * 1) if `signer` is an EOA, then `signature` must be a valid ECDSA signature from `signer`, 26 | * indicating their intention for this action 27 | * 2) if `signer` is a contract, then `signature` must will be checked according to EIP-1271 28 | */ 29 | if (Address.isContract(signer)) { 30 | require( 31 | IERC1271(signer).isValidSignature(digestHash, signature) == EIP1271_MAGICVALUE, 32 | "EIP1271SignatureUtils.checkSignature_EIP1271: ERC1271 signature verification failed" 33 | ); 34 | } else { 35 | require( 36 | ECDSA.recover(digestHash, signature) == signer, 37 | "EIP1271SignatureUtils.checkSignature_EIP1271: signature not from signer" 38 | ); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/contracts/libraries/Endian.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.0; 3 | 4 | library Endian { 5 | /** 6 | * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64 7 | * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type 8 | * @return n The big endian-formatted uint64 9 | * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits) 10 | * through a right-shift/shr operation. 11 | */ 12 | function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) { 13 | // the number needs to be stored in little-endian encoding (ie in bytes 0-8) 14 | n = uint64(uint256(lenum >> 192)); 15 | return 16 | (n >> 56) | 17 | ((0x00FF000000000000 & n) >> 40) | 18 | ((0x0000FF0000000000 & n) >> 24) | 19 | ((0x000000FF00000000 & n) >> 8) | 20 | ((0x00000000FF000000 & n) << 8) | 21 | ((0x0000000000FF0000 & n) << 24) | 22 | ((0x000000000000FF00 & n) << 40) | 23 | ((0x00000000000000FF & n) << 56); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/contracts/permissions/PauserRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../interfaces/IPauserRegistry.sol"; 5 | 6 | /** 7 | * @title Defines pauser & unpauser roles + modifiers to be used elsewhere. 8 | * @author Layr Labs, Inc. 9 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 10 | */ 11 | contract PauserRegistry is IPauserRegistry { 12 | /// @notice Mapping of addresses to whether they hold the pauser role. 13 | mapping(address => bool) public isPauser; 14 | 15 | /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses. 16 | address public unpauser; 17 | 18 | modifier onlyUnpauser() { 19 | require(msg.sender == unpauser, "msg.sender is not permissioned as unpauser"); 20 | _; 21 | } 22 | 23 | constructor(address[] memory _pausers, address _unpauser) { 24 | for (uint256 i = 0; i < _pausers.length; i++) { 25 | _setIsPauser(_pausers[i], true); 26 | } 27 | _setUnpauser(_unpauser); 28 | } 29 | 30 | /// @notice Sets new pauser - only callable by unpauser, as the unpauser is expected to be kept more secure, e.g. being a multisig with a higher threshold 31 | /// @param newPauser Address to be added/removed as pauser 32 | /// @param canPause Whether the address should be added or removed as pauser 33 | function setIsPauser(address newPauser, bool canPause) external onlyUnpauser { 34 | _setIsPauser(newPauser, canPause); 35 | } 36 | 37 | /// @notice Sets new unpauser - only callable by unpauser, as the unpauser is expected to be kept more secure, e.g. being a multisig with a higher threshold 38 | function setUnpauser(address newUnpauser) external onlyUnpauser { 39 | _setUnpauser(newUnpauser); 40 | } 41 | 42 | function _setIsPauser(address pauser, bool canPause) internal { 43 | require(pauser != address(0), "PauserRegistry._setPauser: zero address input"); 44 | isPauser[pauser] = canPause; 45 | emit PauserStatusChanged(pauser, canPause); 46 | } 47 | 48 | function _setUnpauser(address newUnpauser) internal { 49 | require(newUnpauser != address(0), "PauserRegistry._setUnpauser: zero address input"); 50 | emit UnpauserChanged(unpauser, newUnpauser); 51 | unpauser = newUnpauser; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/contracts/pods/EigenPodPausingConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | /** 5 | * @title Constants shared between 'EigenPod' and 'EigenPodManager' contracts. 6 | * @author Layr Labs, Inc. 7 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 8 | */ 9 | abstract contract EigenPodPausingConstants { 10 | /// @notice Index for flag that pauses creation of new EigenPods when set. See EigenPodManager code for details. 11 | uint8 internal constant PAUSED_NEW_EIGENPODS = 0; 12 | /** 13 | * @notice Index for flag that pauses all withdrawal-of-restaked ETH related functionality ` 14 | * function *of the EigenPodManager* when set. See EigenPodManager code for details. 15 | */ 16 | uint8 internal constant PAUSED_WITHDRAW_RESTAKED_ETH = 1; 17 | 18 | /// @notice Index for flag that pauses the deposit related functions *of the EigenPods* when set. see EigenPod code for details. 19 | uint8 internal constant PAUSED_EIGENPODS_VERIFY_CREDENTIALS = 2; 20 | /// @notice Index for flag that pauses the `verifyBalanceUpdate` function *of the EigenPods* when set. see EigenPod code for details. 21 | uint8 internal constant PAUSED_EIGENPODS_VERIFY_BALANCE_UPDATE = 3; 22 | /// @notice Index for flag that pauses the `verifyBeaconChainFullWithdrawal` function *of the EigenPods* when set. see EigenPod code for details. 23 | uint8 internal constant PAUSED_EIGENPODS_VERIFY_WITHDRAWAL = 4; 24 | /// @notice Pausability for EigenPod's "accidental transfer" withdrawal methods 25 | uint8 internal constant PAUSED_NON_PROOF_WITHDRAWALS = 5; 26 | } 27 | -------------------------------------------------------------------------------- /src/contracts/utils/UpgradeableSignatureCheckingUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; 5 | import "../libraries/EIP1271SignatureUtils.sol"; 6 | 7 | /** 8 | * @title Abstract contract that implements minimal signature-related storage & functionality for upgradeable contracts. 9 | * @author Layr Labs, Inc. 10 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 11 | */ 12 | abstract contract UpgradeableSignatureCheckingUtils is Initializable { 13 | /// @notice The EIP-712 typehash for the contract's domain 14 | bytes32 public constant DOMAIN_TYPEHASH = 15 | keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); 16 | 17 | // chain id at the time of contract deployment 18 | uint256 internal immutable ORIGINAL_CHAIN_ID; 19 | 20 | /** 21 | * @notice Original EIP-712 Domain separator for this contract. 22 | * @dev The domain separator may change in the event of a fork that modifies the ChainID. 23 | * Use the getter function `domainSeparator` to get the current domain separator for this contract. 24 | */ 25 | bytes32 internal _DOMAIN_SEPARATOR; 26 | 27 | // INITIALIZING FUNCTIONS 28 | constructor() { 29 | ORIGINAL_CHAIN_ID = block.chainid; 30 | } 31 | 32 | function _initializeSignatureCheckingUtils() internal onlyInitializing { 33 | _DOMAIN_SEPARATOR = _calculateDomainSeparator(); 34 | } 35 | 36 | // VIEW FUNCTIONS 37 | /** 38 | * @notice Getter function for the current EIP-712 domain separator for this contract. 39 | * @dev The domain separator will change in the event of a fork that changes the ChainID. 40 | */ 41 | function domainSeparator() public view returns (bytes32) { 42 | if (block.chainid == ORIGINAL_CHAIN_ID) { 43 | return _DOMAIN_SEPARATOR; 44 | } else { 45 | return _calculateDomainSeparator(); 46 | } 47 | } 48 | 49 | // @notice Internal function for calculating the current domain separator of this contract 50 | function _calculateDomainSeparator() internal view returns (bytes32) { 51 | return keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("EigenLayer")), block.chainid, address(this))); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/Strategy.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "./EigenLayerTestHelper.t.sol"; 5 | import "../contracts/core/StrategyManagerStorage.sol"; 6 | 7 | contract StrategyTests is EigenLayerTestHelper { 8 | /// @notice This function tests to ensure that a delegation contract 9 | /// cannot be intitialized multiple times 10 | function testCannotInitMultipleTimesDelegation() public cannotReinit { 11 | wethStrat.initialize(weth, eigenLayerPauserReg); 12 | } 13 | 14 | ///@notice This function tests to ensure that only the strategyManager 15 | /// can deposit into a strategy 16 | ///@param invalidDepositor is the non-registered depositor 17 | function testInvalidCalltoDeposit(address invalidDepositor) public fuzzedAddress(invalidDepositor) { 18 | IERC20 underlyingToken = wethStrat.underlyingToken(); 19 | 20 | cheats.startPrank(invalidDepositor); 21 | cheats.expectRevert(bytes("StrategyBase.onlyStrategyManager")); 22 | wethStrat.deposit(underlyingToken, 1e18); 23 | cheats.stopPrank(); 24 | } 25 | 26 | ///@notice This function tests to ensure that only the strategyManager 27 | /// can deposit into a strategy 28 | ///@param invalidWithdrawer is the non-registered withdrawer 29 | ///@param depositor is the depositor for which the shares are being withdrawn 30 | function testInvalidCalltoWithdraw(address depositor, address invalidWithdrawer) 31 | public 32 | fuzzedAddress(invalidWithdrawer) 33 | { 34 | IERC20 underlyingToken = wethStrat.underlyingToken(); 35 | 36 | cheats.startPrank(invalidWithdrawer); 37 | cheats.expectRevert(bytes("StrategyBase.onlyStrategyManager")); 38 | wethStrat.withdraw(depositor, underlyingToken, 1e18); 39 | cheats.stopPrank(); 40 | } 41 | 42 | ///@notice This function tests ensures that withdrawing for a depositor that never 43 | /// actually deposited fails. 44 | ///@param depositor is the depositor for which the shares are being withdrawn 45 | function testWithdrawalExceedsTotalShares(address depositor, uint256 shares) public fuzzedAddress(depositor) { 46 | cheats.assume(shares > strategyManager.stakerStrategyShares(depositor, wethStrat)); 47 | IERC20 underlyingToken = wethStrat.underlyingToken(); 48 | 49 | cheats.startPrank(address(strategyManager)); 50 | 51 | cheats.expectRevert( 52 | bytes("StrategyBase.withdraw: amountShares must be less than or equal to totalShares") 53 | ); 54 | wethStrat.withdraw(depositor, underlyingToken, shares); 55 | 56 | cheats.stopPrank(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/events/IAVSDirectoryEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "src/contracts/interfaces/IAVSDirectory.sol"; 5 | 6 | interface IAVSDirectoryEvents { 7 | /** 8 | * @notice Emitted when @param avs indicates that they are updating their MetadataURI string 9 | * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing 10 | */ 11 | event AVSMetadataURIUpdated(address indexed avs, string metadataURI); 12 | 13 | /// @notice Emitted when an operator's registration status for an AVS is updated 14 | event OperatorAVSRegistrationStatusUpdated(address indexed operator, address indexed avs, IAVSDirectory.OperatorAVSRegistrationStatus status); 15 | } 16 | -------------------------------------------------------------------------------- /src/test/events/IDelegationManagerEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "src/contracts/interfaces/IDelegationManager.sol"; 5 | 6 | interface IDelegationManagerEvents { 7 | // @notice Emitted when a new operator registers in EigenLayer and provides their OperatorDetails. 8 | event OperatorRegistered(address indexed operator, IDelegationManager.OperatorDetails operatorDetails); 9 | 10 | // @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails 11 | event OperatorDetailsModified(address indexed operator, IDelegationManager.OperatorDetails newOperatorDetails); 12 | 13 | /** 14 | * @notice Emitted when @param operator indicates that they are updating their MetadataURI string 15 | * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing 16 | */ 17 | event OperatorMetadataURIUpdated(address indexed operator, string metadataURI); 18 | 19 | /** 20 | * @notice Emitted when @param avs indicates that they are updating their MetadataURI string 21 | * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing 22 | */ 23 | event AVSMetadataURIUpdated(address indexed avs, string metadataURI); 24 | 25 | /// @notice Enum representing the status of an operator's registration with an AVS 26 | enum OperatorAVSRegistrationStatus { 27 | UNREGISTERED, // Operator not registered to AVS 28 | REGISTERED // Operator registered to AVS 29 | } 30 | 31 | /// @notice Emitted when an operator's registration status for an AVS is updated 32 | event OperatorAVSRegistrationStatusUpdated(address indexed operator, address indexed avs, OperatorAVSRegistrationStatus status); 33 | 34 | /// @notice Emitted whenever an operator's shares are increased for a given strategy 35 | event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); 36 | 37 | /// @notice Emitted whenever an operator's shares are decreased for a given strategy 38 | event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); 39 | 40 | // @notice Emitted when @param staker delegates to @param operator. 41 | event StakerDelegated(address indexed staker, address indexed operator); 42 | 43 | // @notice Emitted when @param staker undelegates from @param operator. 44 | event StakerUndelegated(address indexed staker, address indexed operator); 45 | 46 | /// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself 47 | event StakerForceUndelegated(address indexed staker, address indexed operator); 48 | 49 | /** 50 | * @notice Emitted when a new withdrawal is queued. 51 | * @param withdrawalRoot Is the hash of the `withdrawal`. 52 | * @param withdrawal Is the withdrawal itself. 53 | */ 54 | event WithdrawalQueued(bytes32 withdrawalRoot, IDelegationManager.Withdrawal withdrawal); 55 | 56 | /// @notice Emitted when a queued withdrawal is completed 57 | event WithdrawalCompleted(bytes32 withdrawalRoot); 58 | 59 | /// @notice Emitted when a queued withdrawal is *migrated* from the StrategyManager to the DelegationManager 60 | event WithdrawalMigrated(bytes32 oldWithdrawalRoot, bytes32 newWithdrawalRoot); 61 | 62 | /// @notice Emitted when the `strategyWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`. 63 | event StrategyWithdrawalDelayBlocksSet(IStrategy strategy, uint256 previousValue, uint256 newValue); 64 | } 65 | -------------------------------------------------------------------------------- /src/test/events/IEigenPodEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | interface IEigenPodEvents { 5 | /// @notice Emitted when an ETH validator stakes via this eigenPod 6 | event EigenPodStaked(bytes pubkey); 7 | 8 | /// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod 9 | event ValidatorRestaked(uint40 validatorIndex); 10 | 11 | /// @notice Emitted when an ETH validator's balance is proven to be updated. Here newValidatorBalanceGwei 12 | // is the validator's balance that is credited on EigenLayer. 13 | event ValidatorBalanceUpdated(uint40 validatorIndex, uint64 balanceTimestamp, uint64 newValidatorBalanceGwei); 14 | 15 | /// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain 16 | event FullWithdrawalRedeemed( 17 | uint40 validatorIndex, 18 | uint64 withdrawalTimestamp, 19 | address indexed recipient, 20 | uint64 withdrawalAmountGwei 21 | ); 22 | 23 | /// @notice Emitted when a partial withdrawal claim is successfully redeemed 24 | event PartialWithdrawalRedeemed( 25 | uint40 validatorIndex, 26 | uint64 withdrawalTimestamp, 27 | address indexed recipient, 28 | uint64 partialWithdrawalAmountGwei 29 | ); 30 | 31 | /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod. 32 | event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount); 33 | 34 | /// @notice Emitted when podOwner enables restaking 35 | event RestakingActivated(address indexed podOwner); 36 | 37 | /// @notice Emitted when ETH is received via the `receive` fallback 38 | event NonBeaconChainETHReceived(uint256 amountReceived); 39 | 40 | /// @notice Emitted when ETH that was previously received via the `receive` fallback is withdrawn 41 | event NonBeaconChainETHWithdrawn(address indexed recipient, uint256 amountWithdrawn); 42 | } 43 | -------------------------------------------------------------------------------- /src/test/events/IEigenPodManagerEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | interface IEigenPodManagerEvents { 5 | /// @notice Emitted to notify the update of the beaconChainOracle address 6 | event BeaconOracleUpdated(address indexed newOracleAddress); 7 | 8 | /// @notice Emitted to notify that the denebForkTimestamp has been set 9 | event DenebForkTimestampUpdated(uint64 denebForkTimestamp); 10 | 11 | /// @notice Emitted to notify the deployment of an EigenPod 12 | event PodDeployed(address indexed eigenPod, address indexed podOwner); 13 | 14 | /// @notice Emitted when the balance of an EigenPod is updated 15 | event PodSharesUpdated(address indexed podOwner, int256 sharesDelta); 16 | } 17 | -------------------------------------------------------------------------------- /src/test/events/IStrategyManagerEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "src/contracts/interfaces/IStrategyManager.sol"; 5 | 6 | interface IStrategyManagerEvents { 7 | /** 8 | * @notice Emitted when a new deposit occurs on behalf of `depositor`. 9 | * @param depositor Is the staker who is depositing funds into EigenLayer. 10 | * @param strategy Is the strategy that `depositor` has deposited into. 11 | * @param token Is the token that `depositor` deposited. 12 | * @param shares Is the number of new shares `depositor` has been granted in `strategy`. 13 | */ 14 | event Deposit(address depositor, IERC20 token, IStrategy strategy, uint256 shares); 15 | 16 | /** 17 | * @notice Emitted when a new withdrawal occurs on behalf of `depositor`. 18 | * @param depositor Is the staker who is queuing a withdrawal from EigenLayer. 19 | * @param nonce Is the withdrawal's unique identifier (to the depositor). 20 | * @param strategy Is the strategy that `depositor` has queued to withdraw from. 21 | * @param shares Is the number of shares `depositor` has queued to withdraw. 22 | */ 23 | event ShareWithdrawalQueued(address depositor, uint96 nonce, IStrategy strategy, uint256 shares); 24 | 25 | /** 26 | * @notice Emitted when a new withdrawal is queued by `depositor`. 27 | * @param depositor Is the staker who is withdrawing funds from EigenLayer. 28 | * @param nonce Is the withdrawal's unique identifier (to the depositor). 29 | * @param withdrawer Is the party specified by `staker` who will be able to complete the queued withdrawal and receive the withdrawn funds. 30 | * @param delegatedAddress Is the party who the `staker` was delegated to at the time of creating the queued withdrawal 31 | * @param withdrawalRoot Is a hash of the input data for the withdrawal. 32 | */ 33 | event WithdrawalQueued( 34 | address depositor, 35 | uint96 nonce, 36 | address withdrawer, 37 | address delegatedAddress, 38 | bytes32 withdrawalRoot 39 | ); 40 | 41 | /// @notice Emitted when a queued withdrawal is completed 42 | event WithdrawalCompleted( 43 | address indexed depositor, 44 | uint96 nonce, 45 | address indexed withdrawer, 46 | bytes32 withdrawalRoot 47 | ); 48 | 49 | /// @notice Emitted when `thirdPartyTransfersForbidden` is updated for a strategy and value by the owner 50 | event UpdatedThirdPartyTransfersForbidden(IStrategy strategy, bool value); 51 | 52 | /// @notice Emitted when the `strategyWhitelister` is changed 53 | event StrategyWhitelisterChanged(address previousAddress, address newAddress); 54 | 55 | /// @notice Emitted when a strategy is added to the approved list of strategies for deposit 56 | event StrategyAddedToDepositWhitelist(IStrategy strategy); 57 | 58 | /// @notice Emitted when a strategy is removed from the approved list of strategies for deposit 59 | event StrategyRemovedFromDepositWhitelist(IStrategy strategy); 60 | 61 | /// @notice Emitted when the `withdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`. 62 | event WithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue); 63 | } 64 | -------------------------------------------------------------------------------- /src/test/harnesses/EigenPodManagerWrapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../contracts/pods/EigenPodManager.sol"; 5 | 6 | ///@notice This contract exposed the internal `_calculateChangeInDelegatableShares` function for testing 7 | contract EigenPodManagerWrapper is EigenPodManager { 8 | 9 | constructor( 10 | IETHPOSDeposit _ethPOS, 11 | IBeacon _eigenPodBeacon, 12 | IStrategyManager _strategyManager, 13 | ISlasher _slasher, 14 | IDelegationManager _delegationManager 15 | ) EigenPodManager(_ethPOS, _eigenPodBeacon, _strategyManager, _slasher, _delegationManager) {} 16 | 17 | function calculateChangeInDelegatableShares(int256 sharesBefore, int256 sharesAfter) external pure returns (int256) { 18 | return _calculateChangeInDelegatableShares(sharesBefore, sharesAfter); 19 | } 20 | 21 | function setPodAddress(address owner, IEigenPod pod) external { 22 | ownerToPod[owner] = pod; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/harnesses/PausableHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../contracts/permissions/Pausable.sol"; 5 | 6 | // wrapper around the Pausable contract that exposes the internal `_initializePauser` function. 7 | contract PausableHarness is Pausable { 8 | function initializePauser(IPauserRegistry _pauserRegistry, uint256 initPausedStatus) external { 9 | _initializePauser(_pauserRegistry, initPausedStatus); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/integration/TimeMachine.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | contract TimeMachine is Test { 7 | 8 | Vm cheats = Vm(HEVM_ADDRESS); 9 | 10 | bool pastExists = false; 11 | uint lastSnapshot; 12 | 13 | uint64 public proofGenStartTime; 14 | 15 | function createSnapshot() public returns (uint) { 16 | uint snapshot = cheats.snapshot(); 17 | lastSnapshot = snapshot; 18 | pastExists = true; 19 | return snapshot; 20 | } 21 | 22 | function warpToLast() public returns (uint curState) { 23 | // Safety check to make sure createSnapshot is called before attempting to warp 24 | // so we don't accidentally prevent our own births 25 | assertTrue(pastExists, "Global.warpToPast: invalid usage, past does not exist"); 26 | 27 | curState = cheats.snapshot(); 28 | cheats.revertTo(lastSnapshot); 29 | return curState; 30 | } 31 | 32 | function warpToPresent(uint curState) public { 33 | cheats.revertTo(curState); 34 | } 35 | 36 | /// @dev Sets the timestamp we use for proof gen to now, 37 | /// then sets block timestamp to now + secondsAgo. 38 | /// 39 | /// This means we can create mock proofs using an oracle time 40 | /// of `proofGenStartTime`. 41 | function setProofGenStartTime(uint secondsAgo) public { 42 | proofGenStartTime = uint64(block.timestamp); 43 | cheats.warp(block.timestamp + secondsAgo); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/integration/deprecatedInterfaces/mainnet/IDelayedWithdrawalRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | /// @notice DEPRECATED INTERFACE at commit hash https://github.com/Layr-Labs/eigenlayer-contracts/tree/0139d6213927c0a7812578899ddd3dda58051928 5 | interface IDelayedWithdrawalRouter_DeprecatedM1 { 6 | // struct used to pack data into a single storage slot 7 | struct DelayedWithdrawal { 8 | uint224 amount; 9 | uint32 blockCreated; 10 | } 11 | 12 | // struct used to store a single users delayedWithdrawal data 13 | struct UserDelayedWithdrawals { 14 | uint256 delayedWithdrawalsCompleted; 15 | DelayedWithdrawal[] delayedWithdrawals; 16 | } 17 | 18 | /** 19 | * @notice Creates an delayed withdrawal for `msg.value` to the `recipient`. 20 | * @dev Only callable by the `podOwner`'s EigenPod contract. 21 | */ 22 | function createDelayedWithdrawal(address podOwner, address recipient) external payable; 23 | 24 | /** 25 | * @notice Called in order to withdraw delayed withdrawals made to the `recipient` that have passed the `withdrawalDelayBlocks` period. 26 | * @param recipient The address to claim delayedWithdrawals for. 27 | * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming. 28 | */ 29 | function claimDelayedWithdrawals(address recipient, uint256 maxNumberOfWithdrawalsToClaim) external; 30 | 31 | /** 32 | * @notice Called in order to withdraw delayed withdrawals made to the caller that have passed the `withdrawalDelayBlocks` period. 33 | * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming. 34 | */ 35 | function claimDelayedWithdrawals(uint256 maxNumberOfWithdrawalsToClaim) external; 36 | 37 | /// @notice Owner-only function for modifying the value of the `withdrawalDelayBlocks` variable. 38 | function setWithdrawalDelayBlocks(uint256 newValue) external; 39 | 40 | /// @notice Getter function for the mapping `_userWithdrawals` 41 | function userWithdrawals(address user) external view returns (UserDelayedWithdrawals memory); 42 | 43 | /// @notice Getter function to get all delayedWithdrawals of the `user` 44 | function getUserDelayedWithdrawals(address user) external view returns (DelayedWithdrawal[] memory); 45 | 46 | /// @notice Getter function to get all delayedWithdrawals that are currently claimable by the `user` 47 | function getClaimableUserDelayedWithdrawals(address user) external view returns (DelayedWithdrawal[] memory); 48 | 49 | /// @notice Getter function for fetching the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array 50 | function userDelayedWithdrawalByIndex(address user, uint256 index) external view returns (DelayedWithdrawal memory); 51 | 52 | /// @notice Getter function for fetching the length of the delayedWithdrawals array of a specific user 53 | function userWithdrawalsLength(address user) external view returns (uint256); 54 | 55 | /// @notice Convenience function for checking whether or not the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array is currently claimable 56 | function canClaimDelayedWithdrawal(address user, uint256 index) external view returns (bool); 57 | 58 | /** 59 | * @notice Delay enforced by this contract for completing any delayedWithdrawal. Measured in blocks, and adjustable by this contract's owner, 60 | * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). 61 | */ 62 | function withdrawalDelayBlocks() external view returns (uint256); 63 | } 64 | -------------------------------------------------------------------------------- /src/test/integration/mocks/BeaconChainOracleMock.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "src/contracts/interfaces/IBeaconChainOracle.sol"; 5 | 6 | contract BeaconChainOracleMock is IBeaconChainOracle { 7 | 8 | mapping(uint64 => bytes32) blockRoots; 9 | 10 | function timestampToBlockRoot(uint timestamp) public view returns (bytes32) { 11 | return blockRoots[uint64(timestamp)]; 12 | } 13 | 14 | function setBlockRoot(uint64 timestamp, bytes32 blockRoot) public { 15 | blockRoots[timestamp] = blockRoot; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/mocks/BeaconChainOracleMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../contracts/interfaces/IBeaconChainOracle.sol"; 5 | 6 | 7 | 8 | contract BeaconChainOracleMock is IBeaconChainOracle { 9 | 10 | bytes32 public mockBeaconChainStateRoot; 11 | 12 | function getOracleBlockRootAtTimestamp() external view returns(bytes32) { 13 | return mockBeaconChainStateRoot; 14 | } 15 | 16 | function setOracleBlockRootAtTimestamp(bytes32 beaconChainStateRoot) external { 17 | mockBeaconChainStateRoot = beaconChainStateRoot; 18 | } 19 | 20 | function timestampToBlockRoot(uint256 /*blockNumber*/) external view returns(bytes32) { 21 | return mockBeaconChainStateRoot; 22 | } 23 | 24 | function isOracleSigner(address /*_oracleSigner*/) external pure returns(bool) { 25 | return true; 26 | } 27 | 28 | function hasVoted(uint64 /*blockNumber*/, address /*oracleSigner*/) external pure returns(bool) { 29 | return true; 30 | } 31 | 32 | function stateRootVotes(uint64 /*blockNumber*/, bytes32 /*stateRoot*/) external pure returns(uint256) { 33 | return 0; 34 | } 35 | 36 | function totalOracleSigners() external pure returns(uint256) { 37 | return 0; 38 | } 39 | 40 | function threshold() external pure returns(uint256) { 41 | return 0; 42 | } 43 | 44 | function setThreshold(uint256 /*_threshold*/) external pure {} 45 | 46 | function addOracleSigners(address[] memory /*_oracleSigners*/) external pure {} 47 | 48 | function removeOracleSigners(address[] memory /*_oracleSigners*/) external pure {} 49 | 50 | function voteForBeaconChainStateRoot(uint64 /*blockNumber*/, bytes32 /*stateRoot*/) external pure {} 51 | 52 | function latestConfirmedOracleBlockNumber() external pure returns(uint64) {} 53 | } 54 | -------------------------------------------------------------------------------- /src/test/mocks/DelayedWithdrawalRouterMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | import "../../contracts/interfaces/IDelayedWithdrawalRouter.sol"; 5 | 6 | contract DelayedWithdrawalRouterMock is IDelayedWithdrawalRouter { 7 | /** 8 | * @notice Creates an delayed withdrawal for `msg.value` to the `recipient`. 9 | * @dev Only callable by the `podOwner`'s EigenPod contract. 10 | */ 11 | function createDelayedWithdrawal(address podOwner, address recipient) external payable{} 12 | 13 | /** 14 | * @notice Called in order to withdraw delayed withdrawals made to the `recipient` that have passed the `withdrawalDelayBlocks` period. 15 | * @param recipient The address to claim delayedWithdrawals for. 16 | * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming. 17 | */ 18 | function claimDelayedWithdrawals(address recipient, uint256 maxNumberOfWithdrawalsToClaim) external{} 19 | 20 | /** 21 | * @notice Called in order to withdraw delayed withdrawals made to the caller that have passed the `withdrawalDelayBlocks` period. 22 | * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming. 23 | */ 24 | function claimDelayedWithdrawals(uint256 maxNumberOfWithdrawalsToClaim) external{} 25 | 26 | /// @notice Owner-only function for modifying the value of the `withdrawalDelayBlocks` variable. 27 | function setWithdrawalDelayBlocks(uint256 newValue) external{} 28 | 29 | /// @notice Getter function for the mapping `_userWithdrawals` 30 | function userWithdrawals(address user) external view returns (UserDelayedWithdrawals memory){} 31 | 32 | /// @notice Getter function to get all delayedWithdrawals of the `user` 33 | function getUserDelayedWithdrawals(address user) external view returns (DelayedWithdrawal[] memory){} 34 | 35 | /// @notice Getter function to get all delayedWithdrawals that are currently claimable by the `user` 36 | function getClaimableUserDelayedWithdrawals(address user) external view returns (DelayedWithdrawal[] memory){} 37 | 38 | /// @notice Getter function for fetching the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array 39 | function userDelayedWithdrawalByIndex(address user, uint256 index) external view returns (DelayedWithdrawal memory){} 40 | 41 | /// @notice Getter function for fetching the length of the delayedWithdrawals array of a specific user 42 | function userWithdrawalsLength(address user) external view returns (uint256){} 43 | 44 | /// @notice Convenience function for checking whether or not the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array is currently claimable 45 | function canClaimDelayedWithdrawal(address user, uint256 index) external view returns (bool){} 46 | 47 | /** 48 | * @notice Delay enforced by this contract for completing any delayedWithdrawal. Measured in blocks, and adjustable by this contract's owner, 49 | * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). 50 | */ 51 | function withdrawalDelayBlocks() external view returns (uint256){} 52 | } 53 | -------------------------------------------------------------------------------- /src/test/mocks/Dummy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | contract EmptyContract { 5 | function foo() public pure returns (uint256) { 6 | return 0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/mocks/ERC20_SetTransferReverting_Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; 5 | 6 | contract ERC20_SetTransferReverting_Mock is ERC20PresetFixedSupply { 7 | 8 | bool public transfersRevert; 9 | 10 | constructor(uint256 initSupply, address initOwner) 11 | ERC20PresetFixedSupply("ERC20_SetTransferReverting_Mock", "ERC20_SetTransferReverting_Mock", initSupply, initOwner) 12 | {} 13 | 14 | function setTransfersRevert(bool _transfersRevert) public { 15 | transfersRevert = _transfersRevert; 16 | } 17 | 18 | function _beforeTokenTransfer(address, address, uint256) internal view override { 19 | if (transfersRevert) { 20 | // revert without message 21 | revert(); 22 | // revert("ERC20_SetTransferReverting_Mock._beforeTokenTransfer: transfersRevert set"); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/mocks/ETHDepositMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../contracts/interfaces/IETHPOSDeposit.sol"; 5 | 6 | 7 | contract ETHPOSDepositMock is IETHPOSDeposit { 8 | 9 | function deposit( 10 | bytes calldata pubkey, 11 | bytes calldata withdrawal_credentials, 12 | bytes calldata signature, 13 | bytes32 deposit_data_root 14 | ) external payable {} 15 | 16 | 17 | function get_deposit_root() external pure returns (bytes32) { 18 | bytes32 root; 19 | return root; 20 | } 21 | 22 | /// @notice Query the current deposit count. 23 | /// @return The deposit count encoded as a little endian 64-bit number. 24 | function get_deposit_count() external pure returns (bytes memory) { 25 | bytes memory root; 26 | return root; 27 | } 28 | 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/test/mocks/EigenPodManagerMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.9; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../../contracts/interfaces/IEigenPodManager.sol"; 6 | 7 | contract EigenPodManagerMock is IEigenPodManager, Test { 8 | IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); 9 | IBeacon public eigenPodBeacon; 10 | IETHPOSDeposit public ethPOS; 11 | 12 | mapping(address => int256) public podShares; 13 | 14 | function slasher() external view returns(ISlasher) {} 15 | 16 | function createPod() external returns(address) {} 17 | 18 | function stake(bytes calldata /*pubkey*/, bytes calldata /*signature*/, bytes32 /*depositDataRoot*/) external payable {} 19 | 20 | function recordBeaconChainETHBalanceUpdate(address /*podOwner*/, int256 /*sharesDelta*/) external pure {} 21 | 22 | function updateBeaconChainOracle(IBeaconChainOracle /*newBeaconChainOracle*/) external pure {} 23 | 24 | function ownerToPod(address /*podOwner*/) external pure returns(IEigenPod) { 25 | return IEigenPod(address(0)); 26 | } 27 | 28 | function getPod(address podOwner) external pure returns(IEigenPod) { 29 | return IEigenPod(podOwner); 30 | } 31 | 32 | function beaconChainOracle() external pure returns(IBeaconChainOracle) { 33 | return IBeaconChainOracle(address(0)); 34 | } 35 | 36 | function getBlockRootAtTimestamp(uint64 /*timestamp*/) external pure returns(bytes32) { 37 | return bytes32(0); 38 | } 39 | 40 | function strategyManager() external pure returns(IStrategyManager) { 41 | return IStrategyManager(address(0)); 42 | } 43 | 44 | function hasPod(address /*podOwner*/) external pure returns (bool) { 45 | return false; 46 | } 47 | 48 | function pause(uint256 /*newPausedStatus*/) external{} 49 | 50 | function pauseAll() external{} 51 | 52 | function paused() external pure returns (uint256) { 53 | return 0; 54 | } 55 | 56 | function paused(uint8 /*index*/) external pure returns (bool) { 57 | return false; 58 | } 59 | 60 | function setPauserRegistry(IPauserRegistry /*newPauserRegistry*/) external {} 61 | 62 | function pauserRegistry() external pure returns (IPauserRegistry) { 63 | return IPauserRegistry(address(0)); 64 | } 65 | 66 | function unpause(uint256 /*newPausedStatus*/) external{} 67 | 68 | function podOwnerShares(address podOwner) external view returns (int256) { 69 | return podShares[podOwner]; 70 | } 71 | 72 | function setPodOwnerShares(address podOwner, int256 shares) external { 73 | podShares[podOwner] = shares; 74 | } 75 | 76 | function addShares(address /*podOwner*/, uint256 shares) external pure returns (uint256) { 77 | // this is the "increase in delegateable tokens" 78 | return (shares); 79 | } 80 | 81 | function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external {} 82 | 83 | function removeShares(address podOwner, uint256 shares) external {} 84 | 85 | function numPods() external view returns (uint256) {} 86 | 87 | function denebForkTimestamp() external pure returns (uint64) { 88 | return type(uint64).max; 89 | } 90 | 91 | function setDenebForkTimestamp(uint64 timestamp) external{} 92 | } -------------------------------------------------------------------------------- /src/test/mocks/EmptyContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | contract EmptyContract { 5 | function foo() public pure returns (uint256) { 6 | return 0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/mocks/LiquidStakingToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | // modified version of https://github.com/itstargetconfirmed/wrapped-ether/blob/master/contracts/WETH.sol 3 | pragma solidity >=0.4.22 <0.9.0; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | /// @notice An implementation of Wrapped Ether. 8 | /// @author Anderson Singh. 9 | 10 | contract WETH is ERC20 { 11 | constructor() ERC20("Wrapped Ether", "WETH") {} 12 | 13 | /// @dev mint tokens for sender based on amount of ether sent. 14 | function deposit() public payable { 15 | _mint(msg.sender, msg.value); 16 | } 17 | 18 | /// @dev withdraw ether based on requested amount and user balance. 19 | function withdraw(uint256 _amount) external { 20 | require(balanceOf(msg.sender) >= _amount, "insufficient balance."); 21 | _burn(msg.sender, _amount); 22 | payable(msg.sender).transfer(_amount); 23 | } 24 | 25 | fallback() external payable { 26 | deposit(); 27 | } 28 | 29 | receive() external payable { 30 | deposit(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/mocks/OwnableMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | contract OwnableMock is Ownable {} 7 | -------------------------------------------------------------------------------- /src/test/mocks/Reenterer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // contract is *modified* from Seaport's test file: https://github.com/ProjectOpenSea/seaport/blob/891b5d4f52b58eb7030597fbb22dca67fd86c4c8/contracts/test/Reenterer.sol 3 | pragma solidity ^0.8.9; 4 | 5 | import "forge-std/Test.sol"; 6 | 7 | contract Reenterer is Test { 8 | Vm cheats = Vm(HEVM_ADDRESS); 9 | 10 | address public target; 11 | uint256 public msgValue; 12 | bytes public callData; 13 | bytes public expectedRevertData; 14 | bytes public dataToReturn; 15 | 16 | event Reentered(bytes returnData); 17 | 18 | function prepare( 19 | address targetToUse, 20 | uint256 msgValueToUse, 21 | bytes memory callDataToUse 22 | ) external { 23 | target = targetToUse; 24 | msgValue = msgValueToUse; 25 | callData = callDataToUse; 26 | } 27 | 28 | // added function that allows writing to `expectedRevertData` 29 | function prepare( 30 | address targetToUse, 31 | uint256 msgValueToUse, 32 | bytes memory callDataToUse, 33 | bytes memory expectedRevertDataToUse 34 | ) external { 35 | target = targetToUse; 36 | msgValue = msgValueToUse; 37 | callData = callDataToUse; 38 | expectedRevertData = expectedRevertDataToUse; 39 | } 40 | 41 | // added function that allows writing to `dataToReturn` 42 | function prepareReturnData(bytes memory returnDataToUse) external { 43 | dataToReturn = returnDataToUse; 44 | } 45 | 46 | receive() external payable { 47 | // added expectrevert logic 48 | if (expectedRevertData.length != 0) { 49 | cheats.expectRevert(expectedRevertData); 50 | } 51 | (bool success, bytes memory returnData) = target.call{ 52 | value: msgValue 53 | }(callData); 54 | 55 | if (!success) { 56 | assembly { 57 | returndatacopy(0, 0, returndatasize()) 58 | revert(0, returndatasize()) 59 | } 60 | } 61 | 62 | emit Reentered(returnData); 63 | 64 | // added dataToReturn logic 65 | uint256 dataToReturnLength = dataToReturn.length; 66 | if (dataToReturnLength > 0) { 67 | bytes memory _dataToReturn = dataToReturn; 68 | assembly { 69 | return(add(_dataToReturn, 32), dataToReturnLength) 70 | } 71 | } 72 | } 73 | 74 | // added fallback function that is a copy of the `receive` function 75 | fallback() external payable { 76 | // added expectRevert logic 77 | if (expectedRevertData.length != 0) { 78 | cheats.expectRevert(expectedRevertData); 79 | } 80 | (bool success, bytes memory returnData) = target.call{ 81 | value: msgValue 82 | }(callData); 83 | 84 | if (!success) { 85 | assembly { 86 | returndatacopy(0, 0, returndatasize()) 87 | revert(0, returndatasize()) 88 | } 89 | } 90 | 91 | emit Reentered(returnData); 92 | 93 | // added dataToReturn logic 94 | uint256 dataToReturnLength = dataToReturn.length; 95 | if (dataToReturnLength > 0) { 96 | bytes memory _dataToReturn = dataToReturn; 97 | assembly { 98 | return(add(_dataToReturn, 32), dataToReturnLength) 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /src/test/mocks/Reverter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.9; 3 | 4 | contract Reverter { 5 | 6 | fallback() external { 7 | revert("Reverter: I am a contract that always reverts"); 8 | } 9 | } -------------------------------------------------------------------------------- /src/test/test-data/owners.json: -------------------------------------------------------------------------------- 1 | { 2 | "numOwners": 10, 3 | "owners": [ 4 | { 5 | "Address": "0x364ea4241059a1880a289ccf6f3e730371e399c2" 6 | }, 7 | { 8 | "Address": "0x164dfa4241059a1880a289ccf6f3e730371e399c2" 9 | }, 10 | { 11 | "Address": "0x264ea4241059a1880a289ccf6f3e730371e399c4" 12 | }, 13 | { 14 | "Address": "0x464ea4241059a1880a289ccf6f3e730371e399c5" 15 | }, 16 | { 17 | "Address": "0x564ea4241059a1880a289ccf6f3e730371e399c6" 18 | }, 19 | { 20 | "Address": "0x864ea4241059a1880a289ccf6f3e730371e399c7" 21 | }, 22 | { 23 | "Address": "0x344ea4241059a1880a289ccf6f3e730371e399c8" 24 | }, 25 | { 26 | "Address": "0x304ea4241059a1880a289ccf6f3e730371e399c9" 27 | }, 28 | { 29 | "Address": "0x364ea4241059a1880a289ccf6f3e730371e399d4" 30 | }, 31 | { 32 | "Address": "0x364ea4241059a1880a289ccf6f3e730371e39955" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /src/test/test-data/reputedOwners.json: -------------------------------------------------------------------------------- 1 | { 2 | "numOwners": 10, 3 | "owners": [ 4 | { 5 | "Address": "0x364ea4241059a1880a289ccf6f3e730371e399c2" 6 | }, 7 | { 8 | "Address": "0x164dfa4241059a1880a289ccf6f3e730371e399c2" 9 | }, 10 | { 11 | "Address": "0x264ea4241059a1880a289ccf6f3e730371e399c4" 12 | }, 13 | { 14 | "Address": "0x464ea4241059a1880a289ccf6f3e730371e399c5" 15 | }, 16 | { 17 | "Address": "0x564ea4241059a1880a289ccf6f3e730371e399c6" 18 | }, 19 | { 20 | "Address": "0x864ea4241059a1880a289ccf6f3e730371e399c7" 21 | }, 22 | { 23 | "Address": "0x344ea4241059a1880a289ccf6f3e730371e399c8" 24 | }, 25 | { 26 | "Address": "0x304ea4241059a1880a289ccf6f3e730371e399c9" 27 | }, 28 | { 29 | "Address": "0x364ea4241059a1880a289ccf6f3e730371e399d4" 30 | }, 31 | { 32 | "Address": "0x364ea4241059a1880a289ccf6f3e730371e39955" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /src/test/utils/EigenLayerUnitTestBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; 5 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 6 | import "src/contracts/permissions/PauserRegistry.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | abstract contract EigenLayerUnitTestBase is Test { 10 | Vm cheats = Vm(HEVM_ADDRESS); 11 | 12 | PauserRegistry public pauserRegistry; 13 | ProxyAdmin public eigenLayerProxyAdmin; 14 | 15 | mapping(address => bool) public addressIsExcludedFromFuzzedInputs; 16 | 17 | address public constant pauser = address(555); 18 | address public constant unpauser = address(556); 19 | 20 | // Helper Functions/Modifiers 21 | modifier filterFuzzedAddressInputs(address fuzzedAddress) { 22 | cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); 23 | _; 24 | } 25 | 26 | function setUp() public virtual { 27 | address[] memory pausers = new address[](1); 28 | pausers[0] = pauser; 29 | pauserRegistry = new PauserRegistry(pausers, unpauser); 30 | eigenLayerProxyAdmin = new ProxyAdmin(); 31 | 32 | addressIsExcludedFromFuzzedInputs[address(pauserRegistry)] = true; 33 | addressIsExcludedFromFuzzedInputs[address(eigenLayerProxyAdmin)] = true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/utils/EigenLayerUnitTestSetup.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "src/test/mocks/StrategyManagerMock.sol"; 5 | import "src/test/mocks/DelegationManagerMock.sol"; 6 | import "src/test/mocks/SlasherMock.sol"; 7 | import "src/test/mocks/EigenPodManagerMock.sol"; 8 | import "src/test/utils/EigenLayerUnitTestBase.sol"; 9 | 10 | abstract contract EigenLayerUnitTestSetup is EigenLayerUnitTestBase { 11 | // Declare Mocks 12 | StrategyManagerMock strategyManagerMock; 13 | DelegationManagerMock public delegationManagerMock; 14 | SlasherMock public slasherMock; 15 | EigenPodManagerMock public eigenPodManagerMock; 16 | 17 | function setUp() public virtual override { 18 | EigenLayerUnitTestBase.setUp(); 19 | strategyManagerMock = new StrategyManagerMock(); 20 | delegationManagerMock = new DelegationManagerMock(); 21 | slasherMock = new SlasherMock(); 22 | eigenPodManagerMock = new EigenPodManagerMock(); 23 | 24 | addressIsExcludedFromFuzzedInputs[address(0)] = true; 25 | addressIsExcludedFromFuzzedInputs[address(strategyManagerMock)] = true; 26 | addressIsExcludedFromFuzzedInputs[address(delegationManagerMock)] = true; 27 | addressIsExcludedFromFuzzedInputs[address(slasherMock)] = true; 28 | addressIsExcludedFromFuzzedInputs[address(eigenPodManagerMock)] = true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/utils/Operators.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/Script.sol"; 6 | import "forge-std/StdJson.sol"; 7 | 8 | contract Operators is Test { 9 | string internal operatorConfigJson; 10 | 11 | constructor() { 12 | operatorConfigJson = vm.readFile("./src/test/test-data/operators.json"); 13 | } 14 | 15 | function operatorPrefix(uint256 index) public pure returns(string memory) { 16 | return string.concat(".operators[", string.concat(vm.toString(index), "].")); 17 | } 18 | 19 | function getNumOperators() public returns(uint256) { 20 | return stdJson.readUint(operatorConfigJson, ".numOperators"); 21 | } 22 | 23 | function getOperatorAddress(uint256 index) public returns(address) { 24 | return stdJson.readAddress(operatorConfigJson, string.concat(operatorPrefix(index), "Address")); 25 | } 26 | 27 | function getOperatorSecretKey(uint256 index) public returns(uint256) { 28 | return readUint(operatorConfigJson, index, "SecretKey"); 29 | } 30 | 31 | function readUint(string memory json, uint256 index, string memory key) public returns (uint256) { 32 | return stringToUint(stdJson.readString(json, string.concat(operatorPrefix(index), key))); 33 | } 34 | 35 | function stringToUint(string memory s) public pure returns (uint256) { 36 | bytes memory b = bytes(s); 37 | uint256 result = 0; 38 | for (uint256 i = 0; i < b.length; i++) { 39 | if (uint256(uint8(b[i])) >= 48 && uint256(uint8(b[i])) <= 57) { 40 | result = result * 10 + (uint256(uint8(b[i])) - 48); 41 | } 42 | } 43 | return result; 44 | } 45 | function setOperatorJsonFilePath(string memory filepath) public { 46 | operatorConfigJson = vm.readFile(filepath); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/utils/Owners.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/Script.sol"; 6 | import "forge-std/StdJson.sol"; 7 | 8 | contract Owners is Test { 9 | string internal ownersConfigJson; 10 | address[] addresses; 11 | 12 | constructor() { 13 | ownersConfigJson = vm.readFile("./src/test/test-data/owners.json"); 14 | } 15 | 16 | function ownerPrefix(uint256 index) public pure returns(string memory) { 17 | return string.concat(".owners[", string.concat(vm.toString(index), "].")); 18 | } 19 | 20 | function getNumOperators() public returns(uint256) { 21 | return stdJson.readUint(ownersConfigJson, ".numOwners"); 22 | } 23 | 24 | function getOwnerAddress(uint256 index) public returns(address) { 25 | return stdJson.readAddress(ownersConfigJson, string.concat(ownerPrefix(index), "Address")); 26 | } 27 | 28 | function getOwnerAddresses() public returns(address[] memory) { 29 | for (uint256 i = 0; i < getNumOperators(); i++) { 30 | addresses.push(getOwnerAddress(i)); 31 | } 32 | return addresses; 33 | } 34 | 35 | function getReputedOwnerAddresses() public returns(address[] memory) { 36 | resetOwnersConfigJson("reputedOwners.json"); 37 | for (uint256 i = 0; i < getNumOperators(); i++) { 38 | addresses.push(getOwnerAddress(i)); 39 | } 40 | return addresses; 41 | } 42 | 43 | function resetOwnersConfigJson(string memory newConfig) public { 44 | ownersConfigJson = vm.readFile(newConfig); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/utils/SignatureCompaction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 5 | 6 | //small library for dealing with efficiently-packed signatures, where parameters v,r,s are packed into vs and r (64 bytes instead of 65) 7 | library SignatureCompaction { 8 | bytes32 internal constant HALF_CURVE_ORDER = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0; 9 | 10 | function ecrecoverPacked(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { 11 | (address recovered, ECDSA.RecoverError err) = ECDSA.tryRecover(hash, r, vs); 12 | require(err == ECDSA.RecoverError.NoError, "error in ecrecoverPacked"); 13 | return recovered; 14 | } 15 | 16 | function packSignature(bytes32 r, bytes32 s, uint8 v) internal pure returns (bytes32, bytes32) { 17 | require(s <= HALF_CURVE_ORDER, "malleable signature, s too high"); 18 | //v parity is a single bit, encoded as either v = 27 or v = 28 -- in order to recover the bit we subtract 27 19 | bytes32 vs = bytes32(uint256(bytes32(uint256(v) - 27) << 255) | uint256(s)); 20 | return (r, vs); 21 | } 22 | 23 | //same as above, except doesn't take 'r' as argument since it is unneeded 24 | function packVS(bytes32 s, uint8 v) internal pure returns (bytes32) { 25 | require(s <= HALF_CURVE_ORDER, "malleable signature, s too high"); 26 | //v parity is a single bit, encoded as either v = 27 or v = 28 -- in order to recover the bit we subtract 27 27 | return bytes32(uint256(bytes32(uint256(v) - 27) << 255) | uint256(s)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/utils/Utils.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.12; 2 | 3 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 4 | import "src/contracts/strategies/StrategyBase.sol"; 5 | 6 | contract Utils { 7 | address constant dummyAdmin = address(uint160(uint256(keccak256("DummyAdmin")))); 8 | 9 | function deployNewStrategy(IERC20 token, IStrategyManager strategyManager, IPauserRegistry pauserRegistry, address admin) public returns (StrategyBase) { 10 | StrategyBase newStrategy = new StrategyBase(strategyManager); 11 | newStrategy = StrategyBase( 12 | address( 13 | new TransparentUpgradeableProxy( 14 | address(newStrategy), 15 | address(admin), 16 | "" 17 | ) 18 | ) 19 | ); 20 | newStrategy.initialize(token, pauserRegistry); 21 | return newStrategy; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .env 4 | 5 | operator1address=$(cast w a --private-key $OPERATOR_1) 6 | operator2address=$(cast w a --private-key $OPERATOR_2) 7 | operator3address=$(cast w a --private-key $OPERATOR_3) 8 | operator4address=$(cast w a --private-key $OPERATOR_4) 9 | 10 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_1 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "registerAsOperator((address,address,uint32), string)" "($operator1address,0x0000000000000000000000000000000000000000,10)" "https://operator.com/123" 11 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_2 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "registerAsOperator((address,address,uint32), string)" "($operator2address,0x0000000000000000000000000000000000000000,0)" "https://123qwe.com/opop" 12 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_3 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "registerAsOperator((address,address,uint32), string)" "($operator3address,0x0000000000000000000000000000000000000000,0)" "https://geezlouise.com/13" 13 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_4 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "registerAsOperator((address,address,uint32), string)" "($operator4address,0x0000000000000000000000000000000000000000,0)" "https://urmama.com/rich_dank" 14 | 15 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_1 0x78697cd4EE4BdE0514fE8a7C61E8cB1A152B5d78 "registerOperatorWithCoordinator(bytes memory, bytes calldata)" "" "" 16 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_2 0x78697cd4EE4BdE0514fE8a7C61E8cB1A152B5d78 "registerOperatorWithCoordinator(bytes memory, bytes calldata)" "" "" 17 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_3 0x78697cd4EE4BdE0514fE8a7C61E8cB1A152B5d78 "registerOperatorWithCoordinator(bytes memory, bytes calldata)" "" "" 18 | # cast send --rpc-url $RPC_URL --private-key $OPERATOR_4 0x78697cd4EE4BdE0514fE8a7C61E8cB1A152B5d78 "registerOperatorWithCoordinator(bytes memory, bytes calldata)" "" "" 19 | 20 | cast send --rpc-url $RPC_URL --private-key $OPERATOR_1 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "updateOperatorMetadataURI(string calldata)" "https://operator-metadata.s3.amazonaws.com/operator_1.json" 21 | cast send --rpc-url $RPC_URL --private-key $OPERATOR_2 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "updateOperatorMetadataURI(string calldata)" "https://operator-metadata.s3.amazonaws.com/operator_2.json" 22 | cast send --rpc-url $RPC_URL --private-key $OPERATOR_3 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "updateOperatorMetadataURI(string calldata)" "https://operator-metadata.s3.amazonaws.com/operator_3.json" 23 | cast send --rpc-url $RPC_URL --private-key $OPERATOR_4 0x45b4c4DAE69393f62e1d14C5fe375792DF4E6332 "updateOperatorMetadataURI(string calldata)" "https://operator-metadata.s3.amazonaws.com/operator_4.json" -------------------------------------------------------------------------------- /testHelpers/cleProofGenerator.js: -------------------------------------------------------------------------------- 1 | const MerkleTree = require('merkletreejs'); 2 | const keccak256 = require('keccak256'); 3 | 4 | const leaves = ['0x12341234123412341234123412341234123412340000000000000000000000000000000000000000000000000000000000000064', 5 | '0x43214321432143214321432143214321432143210000000000000000000000000000000000000000000000000000000000000010', 6 | '0x12341234123412341234123412341234123412350000000000000000000000000000000000000000000000000000000000000064', 7 | '0x43214321432143214321432143214321432143220000000000000000000000000000000000000000000000000000000000000010', 8 | '0x12341234123412341234123412341234123412360000000000000000000000000000000000000000000000000000000000000064', 9 | '0x43214321432143214321432143214321432143230000000000000000000000000000000000000000000000000000000000000010', 10 | '0x12341234123412341234123412341234123412370000000000000000000000000000000000000000000000000000000000000064', 11 | '0x43214321432143214321432143214321432143240000000000000000000000000000000000000000000000000000000000000010',]. 12 | map(value => keccak256(value)) 13 | 14 | var tree = new MerkleTree.MerkleTree(leaves, keccak256, { sortPairs: true }) 15 | 16 | const root = tree.getHexRoot() 17 | const proof = tree.getHexProof(leaves[2]) 18 | 19 | console.log(root, proof, "0x" + leaves[2].toString('hex')) -------------------------------------------------------------------------------- /testHelpers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testhelpers", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "cleProofGenerator.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "keccak256": "^1.0.6", 13 | "merkletreejs": "^0.2.31", 14 | "web3": "^1.7.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /testHelpers/sign.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3'); 2 | const web3 = new Web3() 3 | 4 | main() 5 | 6 | async function main() { 7 | const signature = await web3.eth.accounts.sign("0xfb56e77dd50bc6258879ea2401037cbb4227cf266191e54c9d0f7fa6fbd1e873", "0xe88d9d864d5d731226020c5d2f02b62a4ce2a4534a39c225d32d3db795f83319"); 8 | console.log(signature) 9 | } 10 | 11 | function toEthSignedMessageHash(messageHex) { 12 | const messageBuffer = Buffer.from(messageHex.substring(2), 'hex'); 13 | const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`); 14 | return web3.utils.sha3(Buffer.concat([prefix, messageBuffer])); 15 | } 16 | 17 | 0xd014256b124b583eb14192505340f3c785c31c3412c7daa358072c0b81b85aa5 18 | 0xf4c6a5d675588f311a9061cf6cbf6eacd95a15f42c9d111a9f65767dfe2e6ff8 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "resolveJsonModule": true 9 | } 10 | } 11 | --------------------------------------------------------------------------------