├── .devcontainer ├── Dockerfile ├── devcontainer.json └── install.sh ├── .dockerignore ├── .env.example ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── design.md │ ├── feature_request.md │ ├── implement.md │ ├── research.md │ ├── task.md │ └── test_implementation.md ├── configs │ ├── solhint.json │ └── storage-diff.json ├── pull_request_template.md └── workflows │ ├── certora.yml │ ├── checks.yml │ └── foundry.yml ├── .gitignore ├── .gitmodules ├── .vscode ├── settings.json └── tasks.json ├── .zeus ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── 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 ├── M4 Mainnet (PEPE) - Certora - Aug 2024.pdf ├── M4 Mainnet (PEPE) - Sigma Prime - Jul 2024.pdf ├── Rewards v2 - SigmaPrime - Dec 2024.pdf ├── RewardsCoordinator - Sigma Prime - May 2024.pdf └── Token + Programmatic Incentives - Sigma Prime - Sep 2024.pdf ├── bin ├── compile-bindings.sh ├── install-deps.sh ├── pre-commit.sh ├── source-env.sh ├── storage-diff.sh └── storage-report.sh ├── certora ├── harnesses │ ├── DelegationManagerHarness.sol │ ├── EigenPodHarness.sol │ ├── EigenPodManagerHarness.sol │ ├── PausableHarness.sol │ ├── StrategyManagerHarness.sol │ └── StructuredLinkedListHarness.sol ├── scripts │ ├── core │ │ ├── verifyDelegationManager.sh │ │ └── verifyStrategyManager.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 ├── docs ├── README.md ├── core │ ├── AVSDirectory.md │ ├── AllocationManager.md │ ├── DelegationManager.md │ ├── EigenPod.md │ ├── EigenPodManager.md │ ├── RewardsCoordinator.md │ ├── StrategyManager.md │ └── accounting │ │ ├── SharesAccounting.md │ │ └── SharesAccountingEdgeCases.md ├── experimental │ └── AVS-Guide.md ├── images │ ├── RewardsCoordinator_Merkle_Tree.png │ ├── Staker Flow Diagrams │ │ ├── Complete Withdrawal as Shares.png │ │ ├── Complete Withdrawal as Tokens.png │ │ ├── Delegating.png │ │ ├── Depositing.png │ │ ├── Queue Withdrawal.png │ │ ├── Validator Exits.png │ │ ├── Validator Yield.png │ │ └── diagrams.excalidraw │ └── slashing-model.png └── permissions │ └── PermissionController.md ├── foundry.toml ├── go.mod ├── go.sum ├── mythril.config.json ├── package-lock.json ├── package.json ├── pkg └── bindings │ ├── AVSDirectory │ └── binding.go │ ├── AVSDirectoryStorage │ └── binding.go │ ├── AllocationManager │ └── binding.go │ ├── AllocationManagerStorage │ └── binding.go │ ├── BackingEigen │ └── binding.go │ ├── BeaconChainProofs │ └── binding.go │ ├── BytesLib │ └── binding.go │ ├── DelegationManager │ └── binding.go │ ├── DelegationManagerStorage │ └── binding.go │ ├── Eigen │ └── binding.go │ ├── EigenPod │ └── binding.go │ ├── EigenPodManager │ └── binding.go │ ├── EigenPodManagerStorage │ └── binding.go │ ├── EigenPodPausingConstants │ └── binding.go │ ├── EigenPodStorage │ └── binding.go │ ├── EigenStrategy │ └── binding.go │ ├── Endian │ └── binding.go │ ├── IAVSDirectory │ └── binding.go │ ├── IAVSRegistrar │ └── binding.go │ ├── IAllocationManager │ └── binding.go │ ├── IBackingEigen │ └── binding.go │ ├── IDelegationManager │ └── binding.go │ ├── IETHPOSDeposit │ └── binding.go │ ├── IEigen │ └── binding.go │ ├── IEigenPod │ └── binding.go │ ├── IEigenPodManager │ └── binding.go │ ├── IPausable │ └── binding.go │ ├── IPauserRegistry │ └── binding.go │ ├── IPermissionController │ └── binding.go │ ├── IRewardsCoordinator │ └── binding.go │ ├── IShareManager │ └── binding.go │ ├── ISignatureUtils │ └── binding.go │ ├── IStrategy │ └── binding.go │ ├── IStrategyFactory │ └── binding.go │ ├── IStrategyManager │ └── binding.go │ ├── Merkle │ └── binding.go │ ├── OperatorSetLib │ └── binding.go │ ├── Pausable │ └── binding.go │ ├── PauserRegistry │ └── binding.go │ ├── PermissionController │ └── binding.go │ ├── PermissionControllerMixin │ └── binding.go │ ├── PermissionControllerStorage │ └── binding.go │ ├── RewardsCoordinator │ └── binding.go │ ├── RewardsCoordinatorStorage │ └── binding.go │ ├── SignatureUtils │ └── binding.go │ ├── SlashingLib │ └── binding.go │ ├── Snapshots │ └── binding.go │ ├── StrategyBase │ └── binding.go │ ├── StrategyBaseTVLLimits │ └── binding.go │ ├── StrategyFactory │ └── binding.go │ ├── StrategyFactoryStorage │ └── binding.go │ ├── StrategyManager │ └── binding.go │ └── StrategyManagerStorage │ └── binding.go ├── script ├── .gitignore ├── configs │ ├── devnet │ │ ├── deploy_from_scratch.anvil.config.json │ │ ├── deploy_from_scratch.holesky.config.json │ │ └── deploy_from_scratch.holesky.slashing.config.json │ ├── holesky.json │ ├── local │ │ ├── deploy_from_scratch.anvil.config.json │ │ └── deploy_from_scratch.slashing.anvil.config.json │ ├── mainnet.json │ ├── mainnet │ │ └── mainnet-addresses.config.json │ ├── preprod.json │ └── zipzoop.json ├── deploy │ ├── devnet │ │ └── deploy_from_scratch.s.sol │ └── local │ │ ├── Deploy_From_Scratch.s.sol │ │ └── deploy_from_scratch.slashing.s.sol ├── interfaces │ └── IUpgradeableBeacon.sol ├── output │ └── devnet │ │ ├── M1_MOCK_deployment_data.json │ │ ├── M2_from_scratch_deployment_data.json │ │ └── SLASHING_deploy_from_scratch_deployment_data.json ├── releases │ ├── Env.sol │ ├── README.md │ ├── v1.1.0-slashing │ │ ├── 1-eoa.s.sol │ │ ├── 2-multisig.s.sol │ │ ├── 3-execute.s.sol │ │ └── upgrade.json │ └── v1.1.1-slashing │ │ ├── 1-eoa.s.sol │ │ ├── 2-multisig.s.sol │ │ ├── 3-execute.s.sol │ │ └── upgrade.json ├── tasks │ ├── README.md │ ├── allocate_operatorSet.s.sol │ ├── complete_withdrawal_from_strategy.s.sol │ ├── deposit_into_strategy.s.sol │ ├── register_as_operator.s.sol │ ├── register_operator_to_operatorSet.s.sol │ ├── run.sh │ ├── slash_operatorSet.s.sol │ ├── unpause_avsDirectory.s.sol │ └── withdraw_from_strategy.s.sol └── utils │ ├── ExistingDeploymentParser.sol │ └── validateStorage │ ├── README.md │ ├── validateStorage.ts │ └── validateUpgrade.sh ├── slither.config.json └── src ├── contracts ├── core │ ├── AVSDirectory.sol │ ├── AVSDirectoryStorage.sol │ ├── AllocationManager.sol │ ├── AllocationManagerStorage.sol │ ├── DelegationManager.sol │ ├── DelegationManagerStorage.sol │ ├── RewardsCoordinator.sol │ ├── RewardsCoordinatorStorage.sol │ ├── StrategyManager.sol │ └── StrategyManagerStorage.sol ├── interfaces │ ├── IAVSDirectory.sol │ ├── IAVSRegistrar.sol │ ├── IAllocationManager.sol │ ├── IBackingEigen.sol │ ├── IDelegationManager.sol │ ├── IETHPOSDeposit.sol │ ├── IEigen.sol │ ├── IEigenPod.sol │ ├── IEigenPodManager.sol │ ├── IPausable.sol │ ├── IPauserRegistry.sol │ ├── IPermissionController.sol │ ├── IRewardsCoordinator.sol │ ├── IShareManager.sol │ ├── ISignatureUtils.sol │ ├── IStrategy.sol │ ├── IStrategyFactory.sol │ └── IStrategyManager.sol ├── libraries │ ├── BeaconChainProofs.sol │ ├── BytesLib.sol │ ├── Endian.sol │ ├── Merkle.sol │ ├── OperatorSetLib.sol │ ├── SlashingLib.sol │ └── Snapshots.sol ├── mixins │ ├── PermissionControllerMixin.sol │ └── SignatureUtils.sol ├── permissions │ ├── Pausable.sol │ ├── PauserRegistry.sol │ ├── PermissionController.sol │ └── PermissionControllerStorage.sol ├── pods │ ├── EigenPod.sol │ ├── EigenPodManager.sol │ ├── EigenPodManagerStorage.sol │ ├── EigenPodPausingConstants.sol │ └── EigenPodStorage.sol ├── strategies │ ├── EigenStrategy.sol │ ├── StrategyBase.sol │ ├── StrategyBaseTVLLimits.sol │ ├── StrategyFactory.sol │ └── StrategyFactoryStorage.sol └── token │ ├── BackingEigen.sol │ └── Eigen.sol └── test ├── DevnetLifecycle.t.sol ├── harnesses ├── DelegationManagerHarness.sol ├── EigenHarness.sol ├── 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 │ │ ├── IDelegationManager.sol │ │ ├── IEigenPod.sol │ │ ├── IEigenPodManager.sol │ │ └── IStrategyManager.sol ├── mocks │ ├── BeaconChainMock.t.sol │ └── EIP_4788_Oracle_Mock.t.sol ├── tests │ ├── Delegate_Deposit_Queue_Complete.t.sol │ ├── Deposit_Delegate_Allocate.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 │ └── eigenpod │ │ ├── EigenPod_Slashing_Migration.t.sol │ │ └── VerifyWC_StartCP_CompleteCP.t.sol └── users │ ├── AVS.t.sol │ ├── User.t.sol │ ├── User_M1.t.sol │ └── User_M2.t.sol ├── mocks ├── AVSDirectoryMock.sol ├── AllocationManagerMock.sol ├── DelegationManagerMock.sol ├── Dummy.sol ├── ERC20Mock.sol ├── ERC20_OneWeiFeeOnTransfer.sol ├── ERC20_SetTransferReverting_Mock.sol ├── ETHDepositMock.sol ├── EigenPodManagerMock.sol ├── EigenPodMock.sol ├── EmptyContract.sol ├── LiquidStakingToken.sol ├── MockAVSRegistrar.sol ├── MockDecimals.sol ├── OwnableMock.sol ├── Reenterer.sol ├── Reverter.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 ├── rewardsCoordinator │ ├── processClaimProofs_MaxEarnerAndLeafIndices.json │ ├── processClaimProofs_Root1.json │ ├── processClaimProofs_Root2.json │ ├── processClaimProofs_Root3.json │ ├── processClaimProofs_SingleEarnerLeaf.json │ ├── processClaimProofs_SingleTokenLeaf.json │ └── processClaim_Preprod_Test.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 ├── token ├── EigenTransferRestrictions.t.sol ├── EigenWrapping.t.sol └── bEIGEN.t.sol ├── tree ├── AllocationManagerUnit.tree ├── DelegationManagerUnit.tree ├── EigenPodManagerUnit.tree ├── EigenPodUnit.tree ├── PermissionControllerUnit.tree └── StrategyManagerUnit.tree ├── unit ├── AVSDirectoryUnit.t.sol ├── AllocationManagerUnit.t.sol ├── DelegationUnit.t.sol ├── DeployFromScratch.t.sol ├── EigenPodManagerUnit.t.sol ├── EigenPodUnit.t.sol ├── PausableUnit.t.sol ├── PauserRegistryUnit.t.sol ├── PermissionControllerUnit.t.sol ├── RewardsCoordinatorUnit.t.sol ├── StrategyBaseTVLLimitsUnit.sol ├── StrategyBaseUnit.t.sol ├── StrategyFactoryUnit.t.sol ├── StrategyManagerUnit.t.sol ├── libraries │ ├── BytesLibUnit.t.sol │ └── SnapshotsUnit.t.sol └── mixins │ └── SignatureUtilsUnit.t.sol └── utils ├── ArrayLib.sol ├── EigenLayerUnitTestSetup.sol ├── EigenPodUser.t.sol ├── Logger.t.sol ├── ProofParsing.sol └── Random.sol /.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 \ 8 | ripgrep python3-pip python3-venv python3 9 | -------------------------------------------------------------------------------- /.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 | // Configure tool-specific properties. 9 | "customizations": { 10 | // Configure access control to other repositories 11 | "codespaces": { 12 | "repositories": { 13 | "Layr-Labs/*": { 14 | "permissions": "write-all" 15 | } 16 | } 17 | }, 18 | // Configure properties specific to VS Code. 19 | "vscode": { 20 | // Add the IDs of extensions you want installed when the container is created. 21 | "extensions": [ 22 | "NomicFoundation.hardhat-solidity", 23 | "GitHub.copilot" 24 | ] 25 | } 26 | }, 27 | "containerEnv": { 28 | "PRIVATE_KEY": "${localEnv:PRIVATE_KEY}", 29 | "PUBLIC_KEY": "${localEnv:PUBLIC_KEY}", 30 | "RPC_MAINNET": "${localEnv:RPC_MAINNET}", 31 | "RPC_HOLESKY": "${localEnv:RPC_HOLESKY}" 32 | }, 33 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 34 | // "forwardPorts": [], 35 | // Use 'postCreateCommand' to run commands after the container is created. 36 | "postCreateCommand": "chmod +x ./.devcontainer/install.sh && bash ./.devcontainer/install.sh", 37 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 38 | "remoteUser": "vscode" 39 | } -------------------------------------------------------------------------------- /.devcontainer/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | NVM_DIR=${HOME}/.nvm 6 | NODE_VERSION=v22.3.0 7 | 8 | # Install node 9 | function npm_install { 10 | mkdir -p ${NVM_DIR}/etc 11 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash 12 | bash -c ". ${NVM_DIR}/nvm.sh && nvm install ${NODE_VERSION} && nvm alias default ${NODE_VERSION} && nvm use default" 13 | 14 | NVM_NODE_PATH=${NVM_DIR}/versions/node/${NODE_VERSION} 15 | NODE_PATH=${NVM_NODE_PATH}/lib/node_modules 16 | PATH=${NVM_NODE_PATH}/bin:$PATH 17 | 18 | npm install npm -g 19 | npm install yarn -g 20 | } 21 | 22 | # Install foundry 23 | function foundry_install { 24 | curl -L https://foundry.paradigm.xyz | bash 25 | ~/.foundry/bin/foundryup 26 | } 27 | 28 | npm_install 29 | foundry_install 30 | 31 | export NVM_NODE_PATH=${NVM_DIR}/versions/node/${NODE_VERSION} 32 | export NODE_PATH=${NVM_NODE_PATH}/lib/node_modules 33 | export PATH=${PATH}:${NVM_NODE_PATH}/bin: -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /cache 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | pkg/**/*.go linguist-generated=true -------------------------------------------------------------------------------- /.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/configs/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 | "no-console": "off" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/configs/storage-diff.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": [ 3 | { 4 | "name": "AVSDirectory", 5 | "address": "0x135dda560e946695d6f155dacafc6f1f25c1f5af" 6 | }, 7 | { 8 | "name": "DelegationManager", 9 | "address": "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A" 10 | }, 11 | { 12 | "name": "RewardsCoordinator", 13 | "address": "0x7750d328b314EfFa365A0402CcfD489B80B0adda" 14 | }, 15 | { 16 | "name": "StrategyManager", 17 | "address": "0x858646372CC42E1A627fcE94aa7A7033e7CF075A" 18 | }, 19 | { 20 | "name": "StrategyFactory", 21 | "address": "0x5e4C39Ad7A3E881585e383dB9827EB4811f6F647" 22 | }, 23 | { 24 | "name": "EigenPodManager", 25 | "address": "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338" 26 | }, 27 | { 28 | "name": "EigenPod", 29 | "address": "0xd4018Ce9A041a9c110A9d0383d2b5E1c66Ae1513" 30 | }, 31 | { 32 | "name": "stETH Strategy", 33 | "address": "0x93c4b944D05dfe6df7645A86cd2206016c51564D" 34 | }, 35 | { 36 | "name": "EigenStrategy", 37 | "address": "0xaCB55C530Acdb2849e6d4f36992Cd8c9D50ED8F7" 38 | }, 39 | { 40 | "name": "StrategyBase", 41 | "address": "0x6c6E8aF98a49bBaBCc17ca1dbA6b95c5D58A2ccb" 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **Motivation:** 2 | 3 | *Explain here the context, and why you're making that change. What is the problem you're trying to solve.* 4 | 5 | **Modifications:** 6 | 7 | *Describe the modifications you've done.* 8 | 9 | **Result:** 10 | 11 | *After your change, what will change.* 12 | -------------------------------------------------------------------------------- /.github/workflows/certora.yml: -------------------------------------------------------------------------------- 1 | # name: Certora 2 | 3 | # on: 4 | # workflow_dispatch: 5 | # pull_request: 6 | # branches: 7 | # - dev 8 | # push: 9 | # branches: 10 | # - dev 11 | # - master 12 | # - release-v* 13 | # - formal-verification 14 | # - m2-mainnet 15 | # - testnet-holesky 16 | 17 | # jobs: 18 | # certora: 19 | # name: Test 20 | 21 | # runs-on: ubuntu-latest 22 | # steps: 23 | 24 | # - uses: actions/checkout@v3 25 | # with: 26 | # submodules: recursive 27 | 28 | # - name: Install Foundry 29 | # uses: foundry-rs/foundry-toolchain@v1 30 | # with: 31 | # version: stable 32 | 33 | # - name: Install forge dependencies 34 | # run: forge install 35 | 36 | # - name: Install Python 37 | # uses: actions/setup-python@v2 38 | # with: 39 | # python-version: '3.10' 40 | # cache: 'pip' 41 | 42 | # - name: Install Java 43 | # uses: actions/setup-java@v2 44 | # with: 45 | # distribution: temurin 46 | # java-version: '17' 47 | 48 | # - name: Install Certora CLI 49 | # run: pip install certora-cli 50 | 51 | # - name: Install Solidity Compiler 52 | # run: | 53 | # pip install solc-select 54 | # solc-select use 0.8.27 --always-install 55 | 56 | # - name: Run Certora Verification 57 | # run: | 58 | # for script in $(ls certora/scripts/{,**}/*.sh | grep -v '\WnoCI\W'); do 59 | # bash "$script" 60 | # done 61 | # env: 62 | # CERTORAKEY: ${{ secrets.CERTORAKEY }} 63 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | push: 5 | workflow_dispatch: {} 6 | 7 | permissions: 8 | contents: read 9 | pull-requests: read 10 | 11 | jobs: 12 | commitlint: 13 | name: Commit Linting 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: wagoid/commitlint-github-action@v6 18 | 19 | go-bindings: 20 | name: Bindings 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | - name: Build and validate 27 | if: github.event_name == 'push' 28 | run: | 29 | make docker 30 | docker run -v `pwd`:/build -w /build --rm -i eigenlayer-contracts:latest bash -c "make gha" 31 | if [ ! -z "$(git status --porcelain)" ]; then git diff; git status; exit 1; fi -------------------------------------------------------------------------------- /.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 | script/output/eigenpods.json 12 | 13 | #Hardhat files 14 | cache_hardhat 15 | artifacts 16 | *.lock 17 | #Foundry files 18 | out 19 | cache 20 | 21 | *.DS_Store 22 | 23 | broadcast 24 | 25 | # Deployment tools 26 | /data 27 | .idea/ 28 | 29 | # Certora Outputs 30 | .certora_internal/ 31 | .certora_recent_jobs.json 32 | 33 | #script config file 34 | # script/M1_deploy.config.json 35 | script/output/M1_deployment_data.json 36 | /script/output/M2_deployment_data.json 37 | 38 | # autogenerated docs (you can generate these locally) 39 | /docs/docgen/ 40 | 41 | script/misc 42 | 43 | test.sh 44 | 45 | # Surya outputs 46 | InheritanceGraph.png 47 | surya_report.md 48 | 49 | .idea 50 | 51 | *state.json 52 | deployed_strategies.json 53 | populate_src* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/ds-test"] 2 | path = lib/ds-test 3 | url = https://github.com/dapphub/ds-test 4 | [submodule "lib/forge-std"] 5 | path = lib/forge-std 6 | url = https://github.com/foundry-rs/forge-std 7 | [submodule "lib/openzeppelin-contracts-v4.9.0"] 8 | path = lib/openzeppelin-contracts-v4.9.0 9 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 10 | [submodule "lib/openzeppelin-contracts-upgradeable-v4.9.0"] 11 | path = lib/openzeppelin-contracts-upgradeable-v4.9.0 12 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 13 | [submodule "lib/zeus-templates"] 14 | path = lib/zeus-templates 15 | url = https://github.com/Layr-Labs/zeus-templates 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.packageDefaultDependenciesContractsDirectory": "src", 3 | "solidity.packageDefaultDependenciesDirectory": "lib", 4 | "editor.formatOnSave": true, 5 | "[solidity]": { 6 | "editor.defaultFormatter": "JuanBlanco.solidity" 7 | }, 8 | "solidity.formatter": "forge", 9 | "solidity.compileUsingRemoteVersion": "latest" 10 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "install", 8 | "type": "shell", 9 | "command": "npm install --include=dev", 10 | "options": { 11 | "cwd": "${workspaceFolder}", 12 | }, 13 | "group": { 14 | "kind": "build" 15 | } 16 | }, 17 | { 18 | "label": "fmt", 19 | "type": "shell", 20 | "command": "forge fmt --check src/contracts", 21 | "options": { 22 | "cwd": "${workspaceFolder}" 23 | }, 24 | "dependsOn": "install", 25 | "group": { 26 | "kind": "build" 27 | } 28 | }, 29 | { 30 | "label": "hint", 31 | "type": "shell", 32 | "command": "npm run hint", 33 | "options": { 34 | "cwd": "${workspaceFolder}" 35 | }, 36 | "dependsOn": "fmt", 37 | "group": { 38 | "kind": "build" 39 | } 40 | }, 41 | { 42 | "label": "build", 43 | "type": "shell", 44 | "command": "forge build --sizes", 45 | "options": { 46 | "cwd": "${workspaceFolder}" 47 | }, 48 | "dependsOn": "hint", 49 | "group": { 50 | "kind": "build", 51 | "isDefault": true 52 | } 53 | }, 54 | { 55 | "label": "clean", 56 | "type": "shell", 57 | "command": "forge clean && forge cache clean", 58 | "options": { 59 | "cwd": "${workspaceFolder}" 60 | }, 61 | "dependsOn": "build", 62 | "group": { 63 | "kind": "build", 64 | "isDefault": false 65 | } 66 | }, 67 | { 68 | "label": "test", 69 | "type": "shell", 70 | "command": "forge test -vvv", 71 | "options": { 72 | "cwd": "${workspaceFolder}" 73 | }, 74 | "dependsOn": "hint", 75 | "group": { 76 | "kind": "test", 77 | "isDefault": true 78 | } 79 | }, 80 | ] 81 | } -------------------------------------------------------------------------------- /.zeus: -------------------------------------------------------------------------------- 1 | { 2 | "zeusHost": "https://github.com/Layr-Labs/eigenlayer-contracts-zeus-metadata", 3 | "migrationDirectory": "script/releases" 4 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | Thank you for considering contributing to EigenLayer! To ensure a smooth and efficient collaboration, please adhere to the following guidelines when submitting a pull request (PR): 4 | 5 | ## Requirements 6 | 7 | - Foundry 8 | - Git 9 | - Node.js 10 | 11 | ## Setup Repo 12 | 13 | ```bash 14 | git clone https://github.com/Layr-Labs/eigenlayer-contracts.git 15 | ``` 16 | 17 | ### Install Dependencies 18 | 19 | ```bash 20 | npm install 21 | npx husky install 22 | forge install 23 | ``` 24 | 25 | ### Environment Variables 26 | 27 | Some of the tests and features of this repository require environment variables to be set up. 28 | 29 | Copy the .env.example file to create a .env and populate it with the appropriate environment values that you have control over. 30 | 31 | ## Development Workflow 32 | 33 | - **Testing**: Ensure that your code is well-tested. Write unit and integration tests as appropriate. 34 | - **Branching**: Create a feature branch from the `dev` branch for your work. The `dev` branch contains the most up-to-date code for upcoming releases. 35 | - **Pull Requests**: Once your feature or fix is complete and tested, submit a PR to merge your feature branch into the `dev` branch. 36 | - **Code Review**: After your PR is approved, squash your commits into a single commit before merging to maintain a clean and concise commit history. 37 | 38 | ## Commit Message Format 39 | 40 | When squashing and merging your commits, please follow the commit message convention below: 41 | 42 | ``` 43 | (): (#) 44 | 45 | 46 | 47 | 48 | 49 | 50 | ``` 51 | 52 | - **Type**: Indicates the nature of the change. 53 | - **Scope**: Specifies the section or module of the codebase affected (optional). 54 | - **Subject**: A brief description of the change. 55 | - **PR Number**: Reference to the pull request associated with this change. 56 | - **Motivation**: Describe the context and reason for the change. 57 | - **Modifications**: Detail the specific changes made. 58 | - **Result**: Explain the outcome or effects of the change. 59 | 60 | ### Example 61 | 62 | An example of a commit message: 63 | 64 | ``` 65 | feat(StrategyManager): Implement new withdrawal flow (#123) 66 | 67 | **Motivation:** 68 | 69 | The current withdrawal process is inefficient and leads to delays for users. 70 | 71 | **Modifications:** 72 | 73 | - Refactored the `withdraw` function in `StrategyManager.sol`. 74 | - Updated associated unit tests to reflect changes. 75 | - Modified documentation to explain the new withdrawal process. 76 | 77 | **Result:** 78 | 79 | The withdrawal process is now more efficient, reducing wait times for users. 80 | ``` 81 | 82 | ### Commit Types 83 | 84 | Use the following types for your commit messages: 85 | 86 | - **feat**: A new feature. 87 | - **fix**: A bug fix. 88 | - **docs**: Documentation-only changes. 89 | - **style**: Changes that do not affect the meaning of the code (e.g., formatting). 90 | - **refactor**: A code change that neither fixes a bug nor adds a feature. 91 | - **perf**: A code change that improves performance. 92 | - **test**: Adding missing or correcting existing tests. 93 | - **chore**: Changes to the build process or auxiliary tools and libraries. 94 | 95 | By following these guidelines, you help maintain the quality and readability of the EigenLayer codebase. We appreciate your contributions and look forward to collaborating with you! 96 | 97 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | COPY bin /build-bin 4 | 5 | RUN apt-get update \ 6 | && apt-get install -y \ 7 | make curl git \ 8 | software-properties-common \ 9 | jq sudo 10 | RUN /build-bin/install-deps.sh 11 | RUN apt-get clean && \ 12 | rm -rf /var/lib/apt/lists/* 13 | 14 | WORKDIR /workspaces/eigenlayer-contracts 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CONTAINER_NAME = eigenlayer-contracts 3 | 4 | .PHONY: install-hooks 5 | install-hooks: 6 | cp bin/pre-commit.sh .git/hooks/pre-commit 7 | 8 | .PHONY: install-deps 9 | install-deps: 10 | ./bin/install-deps.sh 11 | 12 | .PHONY: deps 13 | deps: install-hooks install-deps 14 | 15 | .PHONY: compile 16 | compile: 17 | forge b 18 | 19 | .PHONY: bindings 20 | bindings: compile 21 | ./bin/compile-bindings.sh 22 | 23 | .PHONY: all 24 | all: compile bindings 25 | 26 | gha: 27 | git config --global --add safe.directory "*" 28 | forge install 29 | forge b 30 | ./bin/compile-bindings.sh 31 | 32 | docker: 33 | docker build --progress=plain -t ${CONTAINER_NAME}:latest . 34 | 35 | compile-in-docker: 36 | docker run -v $(PWD):/build -w /build --rm -it ${CONTAINER_NAME}:latest bash -c "make compile" 37 | 38 | bindings-in-docker: 39 | docker run -v $(PWD):/build -w /build --rm -it ${CONTAINER_NAME}:latest bash -c "make bindings" 40 | 41 | all-in-docker: 42 | docker run -v $(PWD):/build -w /build --rm -it ${CONTAINER_NAME}:latest bash -c "make all" 43 | 44 | gha-docker: 45 | docker run -v $(PWD):/build -w /build --rm -i ${CONTAINER_NAME}:latest bash -c "make gha" 46 | 47 | storage-report: 48 | bash "bin/storage-report.sh" "docs/storage-report/" 49 | -------------------------------------------------------------------------------- /audits/M1 Mainnet - Diligence - Mar 2023.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/M1 Mainnet - Diligence - Mar 2023.pdf -------------------------------------------------------------------------------- /audits/M1 Mainnet - Sigma Prime - May 2023.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/M1 Mainnet - Sigma Prime - May 2023.pdf -------------------------------------------------------------------------------- /audits/M2 Mainnet - Cantina - Apr 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/M2 Mainnet - Cantina - Apr 2024.pdf -------------------------------------------------------------------------------- /audits/M2 Mainnet - Sigma Prime - Feb 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/M2 Mainnet - Sigma Prime - Feb 2024.pdf -------------------------------------------------------------------------------- /audits/M4 Mainnet (PEPE) - Certora - Aug 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/M4 Mainnet (PEPE) - Certora - Aug 2024.pdf -------------------------------------------------------------------------------- /audits/M4 Mainnet (PEPE) - Sigma Prime - Jul 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/M4 Mainnet (PEPE) - Sigma Prime - Jul 2024.pdf -------------------------------------------------------------------------------- /audits/Rewards v2 - SigmaPrime - Dec 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/Rewards v2 - SigmaPrime - Dec 2024.pdf -------------------------------------------------------------------------------- /audits/RewardsCoordinator - Sigma Prime - May 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/RewardsCoordinator - Sigma Prime - May 2024.pdf -------------------------------------------------------------------------------- /audits/Token + Programmatic Incentives - Sigma Prime - Sep 2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/audits/Token + Programmatic Incentives - Sigma Prime - Sep 2024.pdf -------------------------------------------------------------------------------- /bin/compile-bindings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BINDING_DIR=./pkg/bindings 4 | JSON_DIR=./out 5 | 6 | function create_binding { 7 | contract_name=$1 8 | 9 | mkdir -p $BINDING_DIR/${contract_name} 10 | 11 | contract_json_path="${JSON_DIR}/${contract_name}.sol/${contract_name}.json" 12 | 13 | binding_out_dir="${BINDING_DIR}/${contract_name}" 14 | mkdir -p $binding_out_dir || true 15 | 16 | cat $contract_json_path | jq -r '.abi' > $binding_out_dir/tmp.abi 17 | cat $contract_json_path | jq -r '.bytecode.object' > $binding_out_dir/tmp.bin 18 | 19 | abigen \ 20 | --bin=$binding_out_dir/tmp.bin \ 21 | --abi=$binding_out_dir/tmp.abi \ 22 | --pkg="${contract_name}" \ 23 | --out=$BINDING_DIR/$contract_name/binding.go \ 24 | > /dev/null 2>&1 25 | 26 | if [[ $? == "1" ]]; 27 | then 28 | echo "Failed to generate binding for $contract_json_path" 29 | fi 30 | rm $binding_out_dir/tmp.abi 31 | rm $binding_out_dir/tmp.bin 32 | } 33 | 34 | contracts=$(find src/contracts -type f -name "*.sol" ) 35 | IFS=$'\n' 36 | 37 | for contract_name in $contracts; do 38 | contract_name=$(basename $contract_name .sol) 39 | create_binding $contract_name 40 | done 41 | -------------------------------------------------------------------------------- /bin/install-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | OS=$(uname -s | tr '[:upper:]' '[:lower:]') 4 | ARCH=$(uname -a | tr '[:upper:]' '[:lower:]') 5 | 6 | linuxAmd64="https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.14.6-aadddf3a.tar.gz" 7 | linuxArm64="https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-arm64-1.14.5-0dd173a7.tar.gz" 8 | 9 | 10 | if [[ "$OS" == "linux" ]]; then 11 | sudo apt-get update 12 | sudo apt-get install -y make curl git software-properties-common jq 13 | 14 | if [[ $ARCH == *"x86_64"* ]]; then 15 | curl -L $linuxAmd64 | tar -xz 16 | elif [[ $ARCH == *"aarch64"* ]]; then 17 | curl -L $linuxArm64 | tar -xz 18 | else 19 | echo "Unsupported architecture: $ARCH" 20 | exit 1 21 | fi 22 | elif [[ "$OS" == "darwin" ]]; then 23 | brew tap ethereum/ethereum 24 | brew install libusb ethereum@1.14.5 25 | else 26 | echo "Unsupported OS: $OS" 27 | exit 1 28 | fi 29 | 30 | curl -L https://foundry.paradigm.xyz | bash 31 | 32 | cp -R /root/.foundry/bin/* /usr/local/bin/ 33 | 34 | foundryup 35 | 36 | cp -R /root/.foundry/bin/* /usr/local/bin/ 37 | -------------------------------------------------------------------------------- /bin/pre-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | make bindings 4 | -------------------------------------------------------------------------------- /bin/source-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check for arguments 4 | if [ "$#" -ne 1 ]; then 5 | echo "Usage: $0 [goerli|local]" 6 | return 1 7 | fi 8 | 9 | case $1 in 10 | goerli) 11 | CHAIN_ID=5 12 | EXECUTOR_MULTISIG="0x3d9C2c2B40d890ad53E27947402e977155CD2808" 13 | FOUNDRY_FUZZ_RUNS=1 14 | ;; 15 | local) 16 | CHAIN_ID=31337 17 | FOUNDRY_FUZZ_RUNS=256 18 | ;; 19 | *) 20 | echo "Invalid argument. Usage: $0 [goerli|local]" 21 | return 1 22 | ;; 23 | esac 24 | 25 | # Export environment variables 26 | export CHAIN_ID=$CHAIN_ID 27 | export EXECUTOR_MULTISIG=$EXECUTOR_MULTISIG 28 | export FOUNDRY_FUZZ_RUNS=$FOUNDRY_FUZZ_RUNS 29 | 30 | # Print environment variables 31 | echo "Environment variables set:" 32 | echo "CHAIN_ID: $CHAIN_ID" 33 | echo "EXECUTOR_MULTISIG: $EXECUTOR_MULTISIG" 34 | echo "FOUNDRY_FUZZ_RUNS: $FOUNDRY_FUZZ_RUNS" -------------------------------------------------------------------------------- /bin/storage-report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Default output directory 4 | OUTPUT_DIR=${1:-docs/storage-report} 5 | 6 | # Function to print messages 7 | log() { 8 | echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" 9 | } 10 | 11 | # Function to print error messages 12 | error() { 13 | echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >&2 14 | } 15 | 16 | log "Starting the storage report generation." 17 | 18 | # Create the output directory if it doesn't exist 19 | if ! mkdir -p "$OUTPUT_DIR"; then 20 | error "Failed to create output directory: $OUTPUT_DIR" 21 | exit 1 22 | fi 23 | 24 | log "Output directory is set to: $OUTPUT_DIR" 25 | 26 | # Loop through Solidity files and generate storage report 27 | # NOTE: Ignores `src/contracts/interfaces` & `src/contracts/libraries` since they "should" not contain storage logic. 28 | for file in $(find src/contracts -name "*.sol" ! -path "src/contracts/interfaces/*" ! -path "src/contracts/libraries/*"); do 29 | contract_name=$(basename "$file" .sol) 30 | 31 | # Check if the file exists and is readable 32 | if [ ! -r "$file" ]; then 33 | error "Cannot read file: $file" 34 | continue 35 | fi 36 | 37 | log "Processing contract: $contract_name" 38 | 39 | # Run forge inspect and capture errors 40 | if ! forge inspect "$contract_name" storage --pretty > "$OUTPUT_DIR/$contract_name.md"; then 41 | error "Failed to generate storage report for contract: $contract_name" 42 | else 43 | log "Storage report generated for contract: $contract_name" 44 | fi 45 | done -------------------------------------------------------------------------------- /certora/harnesses/DelegationManagerHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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.27; 3 | 4 | import "../../src/contracts/pods/EigenPod.sol"; 5 | 6 | contract EigenPodHarness is EigenPod { 7 | 8 | constructor( 9 | IETHPOSDeposit _ethPOS, 10 | IEigenPodManager _eigenPodManager, 11 | uint64 _MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, 12 | uint64 _GENESIS_TIME 13 | ) 14 | EigenPod(_ethPOS, _eigenPodManager, _MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, _GENESIS_TIME) {} 15 | 16 | function get_validatorIndex(bytes32 pubkeyHash) public view returns (uint64) { 17 | return _validatorPubkeyHashToInfo[pubkeyHash].validatorIndex; 18 | } 19 | 20 | function get_restakedBalanceGwei(bytes32 pubkeyHash) public view returns (uint64) { 21 | return _validatorPubkeyHashToInfo[pubkeyHash].restakedBalanceGwei; 22 | } 23 | 24 | function get_mostRecentBalanceUpdateTimestamp(bytes32 pubkeyHash) public view returns (uint64) { 25 | return _validatorPubkeyHashToInfo[pubkeyHash].mostRecentBalanceUpdateTimestamp; 26 | } 27 | 28 | function get_podOwnerShares() public view returns (int256) { 29 | return eigenPodManager.podOwnerShares(podOwner); 30 | } 31 | 32 | function get_withdrawableRestakedExecutionLayerGwei() public view returns (uint256) { 33 | return withdrawableRestakedExecutionLayerGwei; 34 | } 35 | 36 | function get_ETH_Balance() public view returns (uint256) { 37 | return address(this).balance; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /certora/harnesses/EigenPodManagerHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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.27; 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.27; 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.27 7 | 8 | certoraRun certora/harnesses/DelegationManagerHarness.sol \ 9 | lib/openzeppelin-contracts-v4.9.0/contracts/token/ERC20/ERC20.sol lib/openzeppelin-contracts-v4.9.0/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/permissions/PauserRegistry.sol \ 12 | --verify DelegationManagerHarness:certora/specs/core/DelegationManager.spec \ 13 | --solc_via_ir \ 14 | --solc_optimize 1 \ 15 | --optimistic_loop \ 16 | --optimistic_fallback \ 17 | --optimistic_hashing \ 18 | --parametric_contracts DelegationManagerHarness \ 19 | $RULE \ 20 | --loop_iter 2 \ 21 | --packages @openzeppelin=lib/openzeppelin-contracts-v4.9.0 @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable-v4.9.0 \ 22 | --msg "DelegationManager $1 $2" \ 23 | -------------------------------------------------------------------------------- /certora/scripts/core/verifyStrategyManager.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.27 7 | 8 | certoraRun certora/harnesses/StrategyManagerHarness.sol \ 9 | lib/openzeppelin-contracts-v4.9.0/contracts/token/ERC20/ERC20.sol lib/openzeppelin-contracts-v4.9.0/contracts/mocks/ERC1271WalletMock.sol \ 10 | src/contracts/pods/EigenPodManager.sol src/contracts/pods/EigenPod.sol \ 11 | src/contracts/strategies/StrategyBase.sol src/contracts/core/DelegationManager.sol \ 12 | src/contracts/permissions/PauserRegistry.sol \ 13 | --verify StrategyManagerHarness:certora/specs/core/StrategyManager.spec \ 14 | --solc_via_ir \ 15 | --solc_optimize 1 \ 16 | --optimistic_loop \ 17 | --optimistic_fallback \ 18 | --optimistic_hashing \ 19 | --parametric_contracts StrategyManagerHarness \ 20 | $RULE \ 21 | --loop_iter 2 \ 22 | --packages @openzeppelin=lib/openzeppelin-contracts-v4.9.0 @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable-v4.9.0 \ 23 | --msg "StrategyManager $1 $2" \ 24 | -------------------------------------------------------------------------------- /certora/scripts/permissions/verifyPausable.sh: -------------------------------------------------------------------------------- 1 | if [[ "$2" ]] 2 | then 3 | RULE="--rule $2" 4 | fi 5 | 6 | solc-select use 0.8.27 7 | 8 | certoraRun certora/harnesses/PausableHarness.sol \ 9 | src/contracts/permissions/PauserRegistry.sol \ 10 | --verify PausableHarness:certora/specs/permissions/Pausable.spec \ 11 | --solc_via_ir \ 12 | --solc_optimize 1 \ 13 | --optimistic_loop \ 14 | --optimistic_fallback \ 15 | --prover_args '-recursionErrorAsAssert false -recursionEntryLimit 3' \ 16 | --loop_iter 3 \ 17 | --link PausableHarness:pauserRegistry=PauserRegistry \ 18 | $RULE \ 19 | --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.27 7 | 8 | # certoraRun certora/harnesses/EigenPodHarness.sol \ 9 | # src/contracts/core/DelegationManager.sol src/contracts/pods/EigenPodManager.sol \ 10 | # src/contracts/permissions/PauserRegistry.sol \ 11 | # src/contracts/core/StrategyManager.sol \ 12 | # src/contracts/strategies/StrategyBase.sol \ 13 | # lib/openzeppelin-contracts-v4.9.0/contracts/token/ERC20/ERC20.sol \ 14 | # lib/openzeppelin-contracts-v4.9.0/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-v4.9.0 @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable-v4.9.0 \ 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.27 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/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-v4.9.0 @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable-v4.9.0 \ 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.27 7 | 8 | certoraRun src/contracts/strategies/StrategyBase.sol \ 9 | lib/openzeppelin-contracts-v4.9.0/contracts/token/ERC20/ERC20.sol \ 10 | src/contracts/core/StrategyManager.sol \ 11 | src/contracts/permissions/PauserRegistry.sol \ 12 | --verify StrategyBase:certora/specs/strategies/StrategyBase.spec \ 13 | --solc_via_ir \ 14 | --solc_optimize 1 \ 15 | --optimistic_loop \ 16 | --optimistic_fallback \ 17 | --prover_args '-recursionErrorAsAssert false -recursionEntryLimit 3' \ 18 | --loop_iter 3 \ 19 | --packages @openzeppelin=lib/openzeppelin-contracts-v4.9.0 @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable-v4.9.0 \ 20 | --link StrategyBase:strategyManager=StrategyManager \ 21 | --parametric_contracts StrategyBase \ 22 | $RULE \ 23 | --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() -------------------------------------------------------------------------------- /docs/images/RewardsCoordinator_Merkle_Tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/RewardsCoordinator_Merkle_Tree.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Complete Withdrawal as Shares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/Staker Flow Diagrams/Complete Withdrawal as Shares.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Complete Withdrawal as Tokens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/Staker Flow Diagrams/Complete Withdrawal as Tokens.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Delegating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/Staker Flow Diagrams/Delegating.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Depositing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/Staker Flow Diagrams/Depositing.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Queue Withdrawal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/Staker Flow Diagrams/Queue Withdrawal.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Validator Exits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/Staker Flow Diagrams/Validator Exits.png -------------------------------------------------------------------------------- /docs/images/Staker Flow Diagrams/Validator Yield.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/Staker Flow Diagrams/Validator Yield.png -------------------------------------------------------------------------------- /docs/images/slashing-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FateMyName/eigenlayer-contracts/d95bb861bdba179cbf17254a1b578016305aa337/docs/images/slashing-model.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Layr-Labs/eigenlayer-contracts 2 | 3 | go 1.21 4 | 5 | require github.com/ethereum/go-ethereum v1.14.0 6 | 7 | require ( 8 | github.com/Microsoft/go-winio v0.6.1 // indirect 9 | github.com/StackExchange/wmi v1.2.1 // indirect 10 | github.com/bits-and-blooms/bitset v1.10.0 // indirect 11 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect 12 | github.com/consensys/bavard v0.1.13 // indirect 13 | github.com/consensys/gnark-crypto v0.12.1 // indirect 14 | github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect 15 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect 16 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect 17 | github.com/ethereum/c-kzg-4844 v1.0.0 // indirect 18 | github.com/fsnotify/fsnotify v1.6.0 // indirect 19 | github.com/go-ole/go-ole v1.3.0 // indirect 20 | github.com/google/uuid v1.3.0 // indirect 21 | github.com/gorilla/websocket v1.4.2 // indirect 22 | github.com/holiman/uint256 v1.2.4 // indirect 23 | github.com/mmcloughlin/addchain v0.4.0 // indirect 24 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect 25 | github.com/supranational/blst v0.3.11 // indirect 26 | github.com/tklauser/go-sysconf v0.3.12 // indirect 27 | github.com/tklauser/numcpus v0.6.1 // indirect 28 | golang.org/x/crypto v0.22.0 // indirect 29 | golang.org/x/mod v0.17.0 // indirect 30 | golang.org/x/sync v0.7.0 // indirect 31 | golang.org/x/sys v0.19.0 // indirect 32 | golang.org/x/tools v0.20.0 // indirect 33 | rsc.io/tmplfunc v0.0.3 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /mythril.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "remappings": [ 3 | "forge-std/=lib/forge-std/src/", 4 | "@openzeppelin/=lib/openzeppelin-contracts-v4.9.0/", 5 | "@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable-v4.9.0/" 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 reusable 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": "forge test -v", 12 | "fmt:check": "forge fmt --check", 13 | "fmt:fix": "forge fmt", 14 | "hint": "solhint -c .github/configs/solhint.json 'contracts/**/*.sol' 'script/**/*.sol'" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/Layr-Labs/eigenlayer-contracts.git" 19 | }, 20 | "author": "", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/Layr-Labs/eigenlayer-contracts/issues" 24 | }, 25 | "homepage": "https://github.com/Layr-Labs/eigenlayer-contracts#readme", 26 | "devDependencies": { 27 | "@commitlint/cli": "^18.2.0", 28 | "@commitlint/config-conventional": "^18.1.0", 29 | "@types/yargs": "^17.0.28", 30 | "dotenv": "^16.3.1", 31 | "fs": "^0.0.1-security", 32 | "husky": "^8.0.3", 33 | "ts-node": "^10.9.1", 34 | "typescript": "^4.9.4", 35 | "yargs": "^17.7.2", 36 | "solhint": "5.0.1" 37 | }, 38 | "dependencies": { 39 | "solidity-docgen": "^0.6.0-beta.32" 40 | } 41 | } -------------------------------------------------------------------------------- /script/.gitignore: -------------------------------------------------------------------------------- 1 | output/* -------------------------------------------------------------------------------- /script/configs/devnet/deploy_from_scratch.anvil.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "maintainer": "samlaf@eigenlabs.org", 3 | "multisig_addresses": { 4 | "operationsMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 5 | "communityMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 6 | "pauserMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 7 | "executorMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 8 | "timelock": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" 9 | }, 10 | "strategies": [ 11 | { 12 | "token_address": "0x0000000000000000000000000000000000000000", 13 | "token_symbol": "WETH", 14 | "max_per_deposit": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 15 | "max_deposits": 115792089237316195423570985008687907853269984665640564039457584007913129639935 16 | } 17 | ], 18 | "strategyManager": { 19 | "init_paused_status": 0, 20 | "init_withdrawal_delay_blocks": 1 21 | }, 22 | "eigenPod": { 23 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 1, 24 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "32000000000" 25 | }, 26 | "eigenPodManager": { 27 | "init_paused_status": 30 28 | }, 29 | "delayedWithdrawalRouter": { 30 | "init_paused_status": 0, 31 | "init_withdrawal_delay_blocks": 1 32 | }, 33 | "slasher": { 34 | "init_paused_status": 0 35 | }, 36 | "delegation": { 37 | "init_paused_status": 0, 38 | "init_withdrawal_delay_blocks": 1 39 | }, 40 | "rewardsCoordinator": { 41 | "init_paused_status": 0, 42 | "CALCULATION_INTERVAL_SECONDS": 604800, 43 | "MAX_REWARDS_DURATION": 6048000, 44 | "MAX_RETROACTIVE_LENGTH": 7776000, 45 | "MAX_FUTURE_LENGTH": 2592000, 46 | "GENESIS_REWARDS_TIMESTAMP": 1710979200, 47 | "rewards_updater_address": "0x18a0f92Ad9645385E8A8f3db7d0f6CF7aBBb0aD4", 48 | "activation_delay": 7200, 49 | "calculation_interval_seconds": 604800, 50 | "global_operator_commission_bips": 1000, 51 | "OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000, 52 | "OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000 53 | }, 54 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa" 55 | } -------------------------------------------------------------------------------- /script/configs/devnet/deploy_from_scratch.holesky.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "maintainer": "samlaf@eigenlabs.org", 3 | "multisig_addresses": { 4 | "operationsMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", 5 | "communityMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", 6 | "pauserMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", 7 | "executorMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", 8 | "timelock": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479" 9 | }, 10 | "strategies": [ 11 | { 12 | "token_address": "0x0000000000000000000000000000000000000000", 13 | "token_symbol": "WETH", 14 | "max_per_deposit": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 15 | "max_deposits": 115792089237316195423570985008687907853269984665640564039457584007913129639935 16 | } 17 | ], 18 | "strategyManager": { 19 | "init_paused_status": 0, 20 | "init_withdrawal_delay_blocks": 1 21 | }, 22 | "eigenPod": { 23 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 1, 24 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "32000000000" 25 | }, 26 | "eigenPodManager": { 27 | "init_paused_status": 30 28 | }, 29 | "delayedWithdrawalRouter": { 30 | "init_paused_status": 0, 31 | "init_withdrawal_delay_blocks": 1 32 | }, 33 | "slasher": { 34 | "init_paused_status": 0 35 | }, 36 | "delegation": { 37 | "init_paused_status": 0, 38 | "init_withdrawal_delay_blocks": 1 39 | }, 40 | "rewardsCoordinator": { 41 | "init_paused_status": 0, 42 | "CALCULATION_INTERVAL_SECONDS": 604800, 43 | "MAX_REWARDS_DURATION": 6048000, 44 | "MAX_RETROACTIVE_LENGTH": 7776000, 45 | "MAX_FUTURE_LENGTH": 2592000, 46 | "GENESIS_REWARDS_TIMESTAMP": 1710979200, 47 | "rewards_updater_address": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", 48 | "activation_delay": 7200, 49 | "calculation_interval_seconds": 604800, 50 | "global_operator_commission_bips": 1000, 51 | "OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000, 52 | "OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000 53 | }, 54 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa" 55 | } -------------------------------------------------------------------------------- /script/configs/devnet/deploy_from_scratch.holesky.slashing.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "multisig_addresses": { 3 | "operationsMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 4 | "communityMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 5 | "pauserMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 6 | "executorMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 7 | "timelock": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07" 8 | }, 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": 115792089237316195423570985008687907853269984665640564039457584007913129639935 19 | }, 20 | "slasher": { 21 | "init_paused_status": 0 22 | }, 23 | "delegation": { 24 | "init_paused_status": 0, 25 | "init_withdrawal_delay_blocks": 1 26 | }, 27 | "rewardsCoordinator": { 28 | "init_paused_status": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 29 | "CALCULATION_INTERVAL_SECONDS": 604800, 30 | "MAX_REWARDS_DURATION": 6048000, 31 | "MAX_RETROACTIVE_LENGTH": 7776000, 32 | "MAX_FUTURE_LENGTH": 2592000, 33 | "GENESIS_REWARDS_TIMESTAMP": 1710979200, 34 | "rewards_updater_address": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 35 | "activation_delay": 7200, 36 | "calculation_interval_seconds": 604800, 37 | "global_operator_commission_bips": 1000, 38 | "OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000, 39 | "OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000 40 | }, 41 | "allocationManager": { 42 | "init_paused_status": 0, 43 | "DEALLOCATION_DELAY": 86400, 44 | "ALLOCATION_CONFIGURATION_DELAY": 600 45 | }, 46 | "ethPOSDepositAddress": "0x4242424242424242424242424242424242424242" 47 | } -------------------------------------------------------------------------------- /script/configs/local/deploy_from_scratch.anvil.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "maintainer": "samlaf@eigenlabs.org", 3 | "multisig_addresses": { 4 | "operationsMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 5 | "communityMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 6 | "pauserMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 7 | "executorMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 8 | "timelock": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" 9 | }, 10 | "strategies": [ 11 | { 12 | "token_address": "0x0000000000000000000000000000000000000000", 13 | "token_symbol": "WETH", 14 | "max_per_deposit": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 15 | "max_deposits": 115792089237316195423570985008687907853269984665640564039457584007913129639935 16 | } 17 | ], 18 | "strategyManager": { 19 | "init_paused_status": 0, 20 | "init_withdrawal_delay_blocks": 1 21 | }, 22 | "eigenPod": { 23 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 1, 24 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "32000000000" 25 | }, 26 | "eigenPodManager": { 27 | "init_paused_status": 30 28 | }, 29 | "delayedWithdrawalRouter": { 30 | "init_paused_status": 0, 31 | "init_withdrawal_delay_blocks": 1 32 | }, 33 | "slasher": { 34 | "init_paused_status": 0 35 | }, 36 | "delegation": { 37 | "init_paused_status": 0, 38 | "init_withdrawal_delay_blocks": 1 39 | }, 40 | "rewardsCoordinator": { 41 | "init_paused_status": 0, 42 | "CALCULATION_INTERVAL_SECONDS": 604800, 43 | "MAX_REWARDS_DURATION": 6048000, 44 | "MAX_RETROACTIVE_LENGTH": 7776000, 45 | "MAX_FUTURE_LENGTH": 2592000, 46 | "GENESIS_REWARDS_TIMESTAMP": 1710979200, 47 | "rewards_updater_address": "0x18a0f92Ad9645385E8A8f3db7d0f6CF7aBBb0aD4", 48 | "activation_delay": 7200, 49 | "calculation_interval_seconds": 604800, 50 | "global_operator_commission_bips": 1000, 51 | "default_operator_split_bips":1000, 52 | "OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000, 53 | "OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000 54 | }, 55 | "allocationManager": { 56 | "init_paused_status": 0, 57 | "DEALLOCATION_DELAY": 900, 58 | "ALLOCATION_CONFIGURATION_DELAY": 1200 59 | }, 60 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa" 61 | } -------------------------------------------------------------------------------- /script/configs/local/deploy_from_scratch.slashing.anvil.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "maintainer": "samlaf@eigenlabs.org", 3 | "multisig_addresses": { 4 | "operationsMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 5 | "communityMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 6 | "pauserMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 7 | "executorMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 8 | "timelock": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" 9 | }, 10 | "strategies": [ 11 | { 12 | "token_address": "0x0000000000000000000000000000000000000000", 13 | "token_symbol": "WETH", 14 | "max_per_deposit": 115792089237316195423570985008687907853269984665640564039457584007913129639935, 15 | "max_deposits": 115792089237316195423570985008687907853269984665640564039457584007913129639935 16 | } 17 | ], 18 | "allocationManager": { 19 | "init_paused_status": 0, 20 | "DEALLOCATION_DELAY": 86400, 21 | "ALLOCATION_CONFIGURATION_DELAY": 600 22 | }, 23 | "strategyManager": { 24 | "init_paused_status": 0, 25 | "init_withdrawal_delay_blocks": 1 26 | }, 27 | "eigenPod": { 28 | "PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 1, 29 | "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "32000000000" 30 | }, 31 | "eigenPodManager": { 32 | "init_paused_status": 30 33 | }, 34 | "delayedWithdrawalRouter": { 35 | "init_paused_status": 0, 36 | "init_withdrawal_delay_blocks": 1 37 | }, 38 | "slasher": { 39 | "init_paused_status": 0 40 | }, 41 | "delegation": { 42 | "withdrawal_delay_blocks": 5, 43 | "init_paused_status": 0, 44 | "init_withdrawal_delay_blocks": 1 45 | }, 46 | "rewardsCoordinator": { 47 | "init_paused_status": 0, 48 | "CALCULATION_INTERVAL_SECONDS": 604800, 49 | "MAX_REWARDS_DURATION": 6048000, 50 | "MAX_RETROACTIVE_LENGTH": 7776000, 51 | "MAX_FUTURE_LENGTH": 2592000, 52 | "GENESIS_REWARDS_TIMESTAMP": 1710979200, 53 | "rewards_updater_address": "0x18a0f92Ad9645385E8A8f3db7d0f6CF7aBBb0aD4", 54 | "activation_delay": 7200, 55 | "calculation_interval_seconds": 604800, 56 | "global_operator_commission_bips": 1000, 57 | "OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000, 58 | "OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000 59 | }, 60 | "ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa" 61 | } -------------------------------------------------------------------------------- /script/interfaces/IUpgradeableBeacon.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | interface IUpgradeableBeacon { 5 | function upgradeTo( 6 | address newImplementation 7 | ) external; 8 | function implementation() external returns (address); 9 | } 10 | -------------------------------------------------------------------------------- /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 | "avsDirectory": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", 4 | "avsDirectoryImplementation": "0x9A676e781A523b5d0C0e43731313A708CB607508", 5 | "baseStrategyImplementation": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", 6 | "delayedWithdrawalRouter": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", 7 | "delayedWithdrawalRouterImplementation": "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE", 8 | "delegation": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", 9 | "delegationImplementation": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", 10 | "eigenLayerPauserReg": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", 11 | "eigenLayerProxyAdmin": "0x5FbDB2315678afecb367f032d93F642f64180aa3", 12 | "eigenPodBeacon": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", 13 | "eigenPodImplementation": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", 14 | "eigenPodManager": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", 15 | "eigenPodManagerImplementation": "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1", 16 | "emptyContract": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", 17 | "rewardsCoordinator": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", 18 | "rewardsCoordinatorImplementation": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed", 19 | "slasher": "0x0165878A594ca255338adfa4d48449f69242Eb8F", 20 | "slasherImplementation": "0x0B306BF915C4d645ff596e518fAf3F9669b97016", 21 | "strategies": "", 22 | "strategyManager": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", 23 | "strategyManagerImplementation": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" 24 | }, 25 | "chainInfo": { 26 | "chainId": 31337, 27 | "deploymentBlock": 0 28 | }, 29 | "parameters": { 30 | "executorMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 31 | "operationsMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" 32 | } 33 | } -------------------------------------------------------------------------------- /script/output/devnet/SLASHING_deploy_from_scratch_deployment_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "addresses": { 3 | "allocationManager": "0xAbD5Dd30CaEF8598d4EadFE7D45Fd582EDEade15", 4 | "allocationManagerImplementation": "0xBFF7154bAa41e702E78Fb082a8Ce257Ce13E6f55", 5 | "avsDirectory": "0xCa839541648D3e23137457b1Fd4A06bccEADD33a", 6 | "avsDirectoryImplementation": "0x1362e9Cb37831C433095f1f1568215B7FDeD37Ef", 7 | "baseStrategyImplementation": "0x61C6A250AEcAbf6b5e4611725b4f99C4DC85DB34", 8 | "delegationManager": "0x3391eBafDD4b2e84Eeecf1711Ff9FC06EF9Ed182", 9 | "delegationManagerImplementation": "0x4073a9B0fb0f31420C2A2263fB6E9adD33ea6F2A", 10 | "eigenLayerPauserReg": "0xBb02ACE793e921D6a454062D2933064F31Fae0B2", 11 | "eigenLayerProxyAdmin": "0xBf0c97a7df334BD83e0912c1218E44FD7953d122", 12 | "eigenPodBeacon": "0x8ad244c2a986e48862c5bE1FdCA27cef0aaa6E15", 13 | "eigenPodImplementation": "0x93cecf40F05389E99e163539F8d1CCbd4267f9A7", 14 | "eigenPodManager": "0x8C9781FD55c67CE4DC08e3035ECbdB2B67a07307", 15 | "eigenPodManagerImplementation": "0x3013B13BF3a464ff9078EFa40b7dbfF8fA67138d", 16 | "emptyContract": "0x689CEE9134e4234caEF6c15Bf1D82779415daFAe", 17 | "rewardsCoordinator": "0xa7DB7B0E63B5B75e080924F9C842758711177c07", 18 | "rewardsCoordinatorImplementation": "0x0e93df1A21CA53F93160AbDee19A92A20f8b397B", 19 | "strategies": [ 20 | { 21 | "strategy_address": "0x4f812633943022fA97cb0881683aAf9f318D5Caa", 22 | "token_address": "0x94373a4919B3240D86eA41593D5eBa789FEF3848", 23 | "token_symbol": "WETH" 24 | } 25 | ], 26 | "strategyBeacon": "0x957c04A5666079255fD75220a15918ecBA6039c6", 27 | "strategyFactory": "0x09F8f1c1ca1815083a8a05E1b4A0c65EFB509141", 28 | "strategyFactoryImplementation": "0x8b1F09f8292fd658Da35b9b3b1d4F7d1C0F3F592", 29 | "strategyManager": "0x70f8bC2Da145b434de66114ac539c9756eF64fb3", 30 | "strategyManagerImplementation": "0x1562BfE7Cb4644ff030C1dE4aA5A9aBb88a61aeC", 31 | "token": { 32 | "tokenProxyAdmin": "0x0000000000000000000000000000000000000000", 33 | "EIGEN": "0x0000000000000000000000000000000000000000", 34 | "bEIGEN": "0x0000000000000000000000000000000000000000", 35 | "EIGENImpl": "0x0000000000000000000000000000000000000000", 36 | "bEIGENImpl": "0x0000000000000000000000000000000000000000", 37 | "eigenStrategy": "0x0000000000000000000000000000000000000000", 38 | "eigenStrategyImpl": "0x0000000000000000000000000000000000000000" 39 | } 40 | }, 41 | "chainInfo": { 42 | "chainId": 17000, 43 | "deploymentBlock": 2548240 44 | }, 45 | "parameters": { 46 | "communityMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 47 | "executorMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 48 | "operationsMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 49 | "pauserMultisig": "0xBB37b72F67A410B76Ce9b9aF9e37aa561B1C5B07", 50 | "timelock": "0x0000000000000000000000000000000000000000" 51 | } 52 | } -------------------------------------------------------------------------------- /script/releases/v1.1.0-slashing/2-multisig.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import {Deploy} from "./1-eoa.s.sol"; 5 | import "../Env.sol"; 6 | 7 | import {MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; 8 | import "zeus-templates/utils/Encode.sol"; 9 | 10 | import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; 11 | 12 | contract Queue is MultisigBuilder, Deploy { 13 | using Env for *; 14 | using Encode for *; 15 | 16 | function _runAsMultisig() 17 | internal 18 | virtual 19 | override 20 | prank(Env.opsMultisig()) 21 | { 22 | bytes memory calldata_to_executor = _getCalldataToExecutor(); 23 | 24 | TimelockController timelock = Env.timelockController(); 25 | timelock.schedule({ 26 | target: Env.executorMultisig(), 27 | value: 0, 28 | data: calldata_to_executor, 29 | predecessor: 0, 30 | salt: 0, 31 | delay: timelock.getMinDelay() 32 | }); 33 | } 34 | 35 | /// @dev Get the calldata to be sent from the timelock to the executor 36 | function _getCalldataToExecutor() internal returns (bytes memory) { 37 | MultisigCall[] storage executorCalls = Encode 38 | .newMultisigCalls() 39 | .append({ 40 | to: Env.proxyAdmin(), 41 | data: Encode.proxyAdmin.upgrade({ 42 | proxy: address(Env.proxy.rewardsCoordinator()), 43 | impl: address(Env.impl.rewardsCoordinator()) 44 | }) 45 | }); 46 | 47 | return 48 | Encode.gnosisSafe.execTransaction({ 49 | from: address(Env.timelockController()), 50 | to: address(Env.multiSendCallOnly()), 51 | op: Encode.Operation.DelegateCall, 52 | data: Encode.multiSend(executorCalls) 53 | }); 54 | } 55 | 56 | function testScript() public virtual { 57 | runAsEOA(); 58 | 59 | TimelockController timelock = Env.timelockController(); 60 | bytes memory calldata_to_executor = _getCalldataToExecutor(); 61 | bytes32 txHash = timelock.hashOperation({ 62 | target: Env.executorMultisig(), 63 | value: 0, 64 | data: calldata_to_executor, 65 | predecessor: 0, 66 | salt: 0 67 | }); 68 | 69 | // Check that the upgrade does not exist in the timelock 70 | assertFalse( 71 | timelock.isOperationPending(txHash), 72 | "Transaction should NOT be queued." 73 | ); 74 | 75 | execute(); 76 | 77 | // Check that the upgrade has been added to the timelock 78 | assertTrue( 79 | timelock.isOperationPending(txHash), 80 | "Transaction should be queued." 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /script/releases/v1.1.0-slashing/upgrade.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slashing-rewards-v2.1", 3 | "from": "1.0.3", 4 | "to": "1.1.0", 5 | "phases": [ 6 | { 7 | "type": "eoa", 8 | "filename": "1-eoa.s.sol" 9 | }, 10 | { 11 | "type": "multisig", 12 | "filename": "2-multisig.s.sol" 13 | }, 14 | { 15 | "type": "multisig", 16 | "filename": "3-execute.s.sol" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /script/releases/v1.1.1-slashing/2-multisig.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import {Deploy} from "./1-eoa.s.sol"; 5 | import "../Env.sol"; 6 | 7 | import {MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; 8 | import "zeus-templates/utils/Encode.sol"; 9 | 10 | import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; 11 | 12 | contract Queue is MultisigBuilder, Deploy { 13 | using Env for *; 14 | using Encode for *; 15 | 16 | function _runAsMultisig() internal virtual override prank(Env.opsMultisig()) { 17 | bytes memory calldata_to_executor = _getCalldataToExecutor(); 18 | 19 | TimelockController timelock = Env.timelockController(); 20 | timelock.schedule({ 21 | target: Env.executorMultisig(), 22 | value: 0, 23 | data: calldata_to_executor, 24 | predecessor: 0, 25 | salt: 0, 26 | delay: timelock.getMinDelay() 27 | }); 28 | } 29 | 30 | /// @dev Get the calldata to be sent from the timelock to the executor 31 | function _getCalldataToExecutor() internal returns (bytes memory) { 32 | MultisigCall[] storage executorCalls = Encode.newMultisigCalls().append({ 33 | to: Env.proxyAdmin(), 34 | data: Encode.proxyAdmin.upgrade({ 35 | proxy: address(Env.proxy.allocationManager()), 36 | impl: address(Env.impl.allocationManager()) 37 | }) 38 | }); 39 | 40 | return Encode.gnosisSafe.execTransaction({ 41 | from: address(Env.timelockController()), 42 | to: address(Env.multiSendCallOnly()), 43 | op: Encode.Operation.DelegateCall, 44 | data: Encode.multiSend(executorCalls) 45 | }); 46 | } 47 | 48 | function testScript() public virtual { 49 | runAsEOA(); 50 | 51 | TimelockController timelock = Env.timelockController(); 52 | bytes memory calldata_to_executor = _getCalldataToExecutor(); 53 | bytes32 txHash = timelock.hashOperation({ 54 | target: Env.executorMultisig(), 55 | value: 0, 56 | data: calldata_to_executor, 57 | predecessor: 0, 58 | salt: 0 59 | }); 60 | 61 | // Check that the upgrade does not exist in the timelock 62 | assertFalse(timelock.isOperationPending(txHash), "Transaction should NOT be queued."); 63 | 64 | execute(); 65 | 66 | // Check that the upgrade has been added to the timelock 67 | assertTrue(timelock.isOperationPending(txHash), "Transaction should be queued."); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /script/releases/v1.1.1-slashing/3-execute.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.12; 3 | 4 | import "../Env.sol"; 5 | import {Queue} from "./2-multisig.s.sol"; 6 | 7 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 8 | import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; 9 | 10 | contract Execute is Queue { 11 | using Env for *; 12 | 13 | function _runAsMultisig() internal override(Queue) prank(Env.protocolCouncilMultisig()) { 14 | bytes memory calldata_to_executor = _getCalldataToExecutor(); 15 | 16 | TimelockController timelock = Env.timelockController(); 17 | timelock.execute({ 18 | target: Env.executorMultisig(), 19 | value: 0, 20 | payload: calldata_to_executor, 21 | predecessor: 0, 22 | salt: 0 23 | }); 24 | } 25 | 26 | function testScript() public virtual override(Queue) { 27 | // 0. Deploy Impls 28 | runAsEOA(); 29 | 30 | TimelockController timelock = Env.timelockController(); 31 | bytes memory calldata_to_executor = _getCalldataToExecutor(); 32 | bytes32 txHash = timelock.hashOperation({ 33 | target: Env.executorMultisig(), 34 | value: 0, 35 | data: calldata_to_executor, 36 | predecessor: 0, 37 | salt: 0 38 | }); 39 | assertFalse(timelock.isOperationPending(txHash), "Transaction should NOT be queued."); 40 | 41 | // 1. Queue Upgrade 42 | Queue._runAsMultisig(); 43 | _unsafeResetHasPranked(); // reset hasPranked so we can use it again 44 | 45 | // 2. Warp past delay 46 | vm.warp(block.timestamp + timelock.getMinDelay()); // 1 tick after ETA 47 | assertEq(timelock.isOperationReady(txHash), true, "Transaction should be executable."); 48 | 49 | // 3- execute 50 | execute(); 51 | 52 | assertTrue(timelock.isOperationDone(txHash), "Transaction should be complete."); 53 | 54 | // 4. Validate 55 | _validateNewImplAddresses(true); 56 | _validateProxyConstructors(); 57 | _validateProxyInitialized(); 58 | } 59 | 60 | function _validateProxyConstructors() internal view { 61 | AllocationManager allocationManager = Env.proxy.allocationManager(); 62 | assertTrue(allocationManager.delegation() == Env.proxy.delegationManager(), "alm.dm invalid"); 63 | assertTrue(allocationManager.pauserRegistry() == Env.impl.pauserRegistry(), "alm.pR invalid"); 64 | assertTrue(allocationManager.permissionController() == Env.proxy.permissionController(), "alm.pc invalid"); 65 | assertTrue( 66 | allocationManager.DEALLOCATION_DELAY() == Env.MIN_WITHDRAWAL_DELAY(), "alm.deallocationDelay invalid" 67 | ); 68 | assertTrue( 69 | allocationManager.ALLOCATION_CONFIGURATION_DELAY() == Env.ALLOCATION_CONFIGURATION_DELAY(), 70 | "alm.allocationConfigurationDelay invalid" 71 | ); 72 | } 73 | 74 | /// @dev Call initialize on all deployed proxies to ensure initializers are disabled 75 | function _validateProxyInitialized() internal { 76 | bytes memory errInit = "Initializable: contract is already initialized"; 77 | 78 | AllocationManager allocationManager = Env.proxy.allocationManager(); 79 | vm.expectRevert(errInit); 80 | allocationManager.initialize(address(0), 0); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /script/releases/v1.1.1-slashing/upgrade.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slashing-registrar-hotfix", 3 | "from": "1.1.0", 4 | "to": "1.1.1", 5 | "phases": [ 6 | { 7 | "type": "eoa", 8 | "filename": "1-eoa.s.sol" 9 | }, 10 | { 11 | "type": "multisig", 12 | "filename": "2-multisig.s.sol" 13 | }, 14 | { 15 | "type": "multisig", 16 | "filename": "3-execute.s.sol" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /script/tasks/allocate_operatorSet.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../src/contracts/core/AVSDirectory.sol"; 5 | import "../../src/contracts/core/AllocationManager.sol"; 6 | 7 | import "forge-std/Script.sol"; 8 | import "forge-std/Test.sol"; 9 | 10 | // use forge: 11 | // RUST_LOG=forge,foundry=trace forge script script/tasks/allocate_operatorSet.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address strategy,address avs,uint32 operatorSetId,uint64 magnitude)" -- 12 | // RUST_LOG=forge,foundry=trace forge script script/tasks/allocate_operatorSet.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address strategy,address avs,uint32 operatorSetId,uint64 magnitude)" -- local/slashing_output.json 0x8aCd85898458400f7Db866d53FCFF6f0D49741FF 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 00000001 0500000000000000000 13 | contract AllocateOperatorSet is Script, Test { 14 | Vm cheats = Vm(VM_ADDRESS); 15 | 16 | function run(string memory configFile, address strategy, address avs, uint32 operatorSetId, uint64 magnitude) public { 17 | // Load config 18 | string memory deployConfigPath = string(bytes(string.concat("script/output/", configFile))); 19 | string memory config_data = vm.readFile(deployConfigPath); 20 | 21 | // Pull allocationManager address 22 | address allocationManager = stdJson.readAddress(config_data, ".addresses.allocationManager"); 23 | 24 | // START RECORDING TRANSACTIONS FOR DEPLOYMENT 25 | vm.startBroadcast(); 26 | 27 | // Attach to the AllocationManager 28 | AllocationManager am = AllocationManager(allocationManager); 29 | 30 | // Correct array initialization 31 | IStrategy[] memory strategies = new IStrategy[](1); 32 | strategies[0] = IStrategy(strategy); 33 | 34 | // Set OperatorSets 35 | OperatorSet[] memory sets = new OperatorSet[](1); 36 | sets[0] = OperatorSet({ 37 | avs: avs, 38 | id: operatorSetId 39 | }); 40 | 41 | // Set new mangitudes 42 | uint64[] memory magnitudes = new uint64[](1); 43 | magnitudes[0] = magnitude; 44 | 45 | // Define a single MagnitudeAllocation and wrap it in an array 46 | IAllocationManagerTypes.AllocateParams[] memory allocations = new IAllocationManagerTypes.AllocateParams[](1); 47 | allocations[0] = IAllocationManagerTypes.AllocateParams({ 48 | operatorSet: sets[0], 49 | strategies: strategies, 50 | newMagnitudes: magnitudes 51 | }); 52 | 53 | // Perform allocation 54 | am.modifyAllocations(msg.sender, allocations); 55 | 56 | // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT 57 | vm.stopBroadcast(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /script/tasks/deposit_into_strategy.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../src/contracts/core/StrategyManager.sol"; 5 | 6 | import "forge-std/Script.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | // use cast: 10 | // 11 | // cast send "approve(address,uint256)" \ 12 | // \ 13 | // \ 14 | // --private-key 15 | // 16 | // cast send "depositIntoStrategy(address,address,uint256)" \ 17 | // \ 18 | // \ 19 | // \ 20 | // --private-key 21 | 22 | // use forge: 23 | // RUST_LOG=forge,foundry=trace forge script script/tasks/deposit_into_strategy.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address strategy,address token,uint256 amount)" -- 24 | // RUST_LOG=forge,foundry=trace forge script script/tasks/deposit_into_strategy.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address strategy,address token,uint256 amount)" -- local/slashing_output.json 0x8aCd85898458400f7Db866d53FCFF6f0D49741FF 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 $DEPOSIT_SHARES 25 | contract DepositIntoStrategy is Script, Test { 26 | Vm cheats = Vm(VM_ADDRESS); 27 | 28 | function run(string memory configFile, address strategy, address token, uint256 amount) public { 29 | // Load config 30 | string memory deployConfigPath = string(bytes(string.concat("script/output/", configFile))); 31 | string memory config_data = vm.readFile(deployConfigPath); 32 | 33 | // Pull strategy manager address 34 | address strategyManager = stdJson.readAddress(config_data, ".addresses.strategyManager"); 35 | 36 | // START RECORDING TRANSACTIONS FOR DEPLOYMENT 37 | vm.startBroadcast(); 38 | 39 | IERC20 tkn = IERC20(token); 40 | StrategyManager sm = StrategyManager(strategyManager); 41 | 42 | // approve spend 43 | tkn.approve(strategyManager, amount); 44 | 45 | // do deposit 46 | sm.depositIntoStrategy(IStrategy(strategy), IERC20(token), amount); 47 | 48 | vm.stopBroadcast(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /script/tasks/register_as_operator.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../src/contracts/core/DelegationManager.sol"; 5 | 6 | import "forge-std/Script.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | // use cast: 10 | // cast send "registerAsOperator((address,address,uint32),uint256,string)" \ 11 | // "(address(0), , 0)" \ 12 | // 0 \ 13 | // "" \ 14 | // --private-key 15 | 16 | // use forge: 17 | // RUST_LOG=forge,foundry=trace forge script script/tasks/register_as_operator.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address operator,string memory metadataURI)" -- 18 | // RUST_LOG=forge,foundry=trace forge script script/tasks/register_as_operator.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address operator,string metadataURI)" -- local/slashing_output.json 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 "test" 19 | contract RegisterAsOperator is Script, Test { 20 | Vm cheats = Vm(VM_ADDRESS); 21 | 22 | function run(string memory configFile, address operator, string memory metadataURI) public { 23 | // Load config 24 | string memory deployConfigPath = string(bytes(string.concat("script/output/", configFile))); 25 | string memory config_data = vm.readFile(deployConfigPath); 26 | 27 | // Pull delegation manager address 28 | address delegationManager = stdJson.readAddress(config_data, ".addresses.delegationManager"); 29 | 30 | // START RECORDING TRANSACTIONS FOR DEPLOYMENT 31 | vm.startBroadcast(); 32 | 33 | // Attach the delegationManager 34 | DelegationManager delegation = DelegationManager(delegationManager); 35 | 36 | // Register the sender as an Operator 37 | delegation.registerAsOperator(operator, 0, metadataURI); 38 | 39 | // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT 40 | vm.stopBroadcast(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /script/tasks/slash_operatorSet.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../src/contracts/core/AllocationManager.sol"; 5 | 6 | import "forge-std/Script.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | // use forge: 10 | // RUST_LOG=forge,foundry=trace forge script script/tasks/slash_operatorSet.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address operator,uint32 operatorSetId,uint256 wadToSlash)" -- 11 | // RUST_LOG=forge,foundry=trace forge script script/tasks/slash_operatorSet.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address operator,uint32 operatorSetId,uint256 wadToSlash)" -- local/slashing_output.json 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 00000001 05000000 12 | contract SlashOperatorSet is Script, Test, IAllocationManagerTypes { 13 | Vm cheats = Vm(VM_ADDRESS); 14 | 15 | function run( 16 | string memory configFile, 17 | address operator, 18 | uint32 operatorSetId, 19 | IStrategy[] memory strategies, 20 | uint256[] memory wadsToSlash 21 | ) public { 22 | // Load config 23 | string memory deployConfigPath = string(bytes(string.concat("script/output/", configFile))); 24 | string memory config_data = vm.readFile(deployConfigPath); 25 | 26 | // Pull allocationManager address 27 | address allocationManager = stdJson.readAddress(config_data, ".addresses.allocationManager"); 28 | 29 | // START RECORDING TRANSACTIONS FOR DEPLOYMENT 30 | vm.startBroadcast(); 31 | 32 | // Attach to the AllocationManager 33 | AllocationManager am = AllocationManager(allocationManager); 34 | 35 | // Define SlashingParams struct instance with correct array initialization 36 | SlashingParams memory slashing = SlashingParams({ 37 | operator: operator, 38 | operatorSetId: operatorSetId, 39 | strategies: strategies, 40 | wadsToSlash: wadsToSlash, 41 | description: "slashed" 42 | }); 43 | 44 | // Perform slashing 45 | am.slashOperator(operator, slashing); 46 | 47 | // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT 48 | vm.stopBroadcast(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /script/tasks/unpause_avsDirectory.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../src/contracts/core/AVSDirectory.sol"; 5 | 6 | import "forge-std/Script.sol"; 7 | import "forge-std/Test.sol"; 8 | 9 | // use cast: 10 | // 11 | // cast send "approve(address,uint256)" \ 12 | // \ 13 | // \ 14 | // --private-key 15 | // 16 | // cast send "depositIntoStrategy(address,address,uint256)" \ 17 | // \ 18 | // \ 19 | // \ 20 | // --private-key 21 | 22 | // use forge: 23 | // RUST_LOG=forge,foundry=trace forge script script/tasks/unpause_avsDirectory.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile)" -- 24 | // RUST_LOG=forge,foundry=trace forge script script/tasks/unpause_avsDirectory.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile)" -- local/slashing_output.json 25 | contract UnpauseAVSDirectory is Script, Test { 26 | Vm cheats = Vm(VM_ADDRESS); 27 | 28 | function run(string memory configFile) public { 29 | // Load config 30 | string memory deployConfigPath = string(bytes(string.concat("script/output/", configFile))); 31 | string memory config_data = vm.readFile(deployConfigPath); 32 | 33 | // Pull avs directory address 34 | address avsDir = stdJson.readAddress(config_data, ".addresses.avsDirectory"); 35 | 36 | // START RECORDING TRANSACTIONS FOR DEPLOYMENT 37 | vm.startBroadcast(); 38 | 39 | // Attach to the AVSDirectory 40 | AVSDirectory avsDirectory = AVSDirectory(avsDir); 41 | 42 | // Unpause the AVSDirectory 43 | avsDirectory.unpause(0); 44 | 45 | // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT 46 | vm.stopBroadcast(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /script/tasks/withdraw_from_strategy.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../src/contracts/core/AllocationManager.sol"; 5 | import "../../src/contracts/core/DelegationManager.sol"; 6 | import "../../src/contracts/libraries/SlashingLib.sol"; 7 | 8 | import "forge-std/Script.sol"; 9 | import "forge-std/Test.sol"; 10 | 11 | // use forge: 12 | // RUST_LOG=forge,foundry=trace forge script script/tasks/withdraw_from_strategy.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address strategy,address token,uint256 amount)" -- 13 | // RUST_LOG=forge,foundry=trace forge script script/tasks/withdraw_from_strategy.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address strategy,address token,uint256 amount)" -- local/slashing_output.json 0x8aCd85898458400f7Db866d53FCFF6f0D49741FF 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 750 14 | contract WithdrawFromStrategy is Script, Test { 15 | Vm cheats = Vm(VM_ADDRESS); 16 | 17 | function run(string memory configFile, address strategy, address token, uint256 amount) public { 18 | // Load config 19 | string memory deployConfigPath = string(bytes(string.concat("script/output/", configFile))); 20 | string memory config_data = vm.readFile(deployConfigPath); 21 | 22 | // Pull addresses from config 23 | // address allocationManager = stdJson.readAddress(config_data, ".addresses.allocationManager"); 24 | address delegationManager = stdJson.readAddress(config_data, ".addresses.delegationManager"); 25 | 26 | // START RECORDING TRANSACTIONS FOR DEPLOYMENT 27 | vm.startBroadcast(); 28 | 29 | // Attach to DelegationManager 30 | // AllocationManager am = AllocationManager(allocationManager); 31 | DelegationManager dm = DelegationManager(delegationManager); 32 | 33 | // Add strategy to array 34 | IStrategy[] memory strategies = new IStrategy[](1); 35 | strategies[0] = IStrategy(strategy); 36 | // Add shares to array 37 | uint256[] memory shares = new uint256[](1); 38 | shares[0] = amount; 39 | // Add token to array 40 | IERC20[] memory tokens = new IERC20[](1); 41 | tokens[0] = IERC20(token); 42 | 43 | // Get the current withdrawal nonce for sender 44 | uint256 nonce = dm.cumulativeWithdrawalsQueued(msg.sender); 45 | 46 | // Define QueuedWithdrawalParams struct instance 47 | IDelegationManagerTypes.QueuedWithdrawalParams[] memory queueWithdrawals = new IDelegationManagerTypes.QueuedWithdrawalParams[](1); 48 | queueWithdrawals[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ 49 | strategies: strategies, 50 | depositShares: shares, 51 | __deprecated_withdrawer: address(0) 52 | }); 53 | 54 | // Withdrawal roots will be returned when we queue 55 | bytes32[] memory withdrawalRoots; 56 | 57 | // Log the details we need to reproduce the WithdrawalRoot 58 | emit log_named_uint("nonce", nonce); 59 | emit log_named_uint("startBlock", block.number + 1); 60 | 61 | // Queue withdrawal 62 | withdrawalRoots = dm.queueWithdrawals(queueWithdrawals); 63 | 64 | // Log the withdrawalRoot 65 | emit log_named_bytes32("withdrawalRoot", withdrawalRoots[0]); 66 | 67 | // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT 68 | vm.stopBroadcast(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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-v4.9.0/", 7 | "@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable-v4.9.0/", 8 | "ds-test/=lib/ds-test/src/" 9 | ], 10 | "compile_force_framework": "foundry" 11 | } -------------------------------------------------------------------------------- /src/contracts/core/AVSDirectoryStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../interfaces/IAVSDirectory.sol"; 5 | import "../interfaces/IDelegationManager.sol"; 6 | 7 | abstract contract AVSDirectoryStorage is IAVSDirectory { 8 | // Constants 9 | 10 | /// @notice The EIP-712 typehash for the `Registration` struct used by the contract 11 | bytes32 public constant OPERATOR_AVS_REGISTRATION_TYPEHASH = 12 | keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)"); 13 | 14 | /// @notice The EIP-712 typehash for the `OperatorSetRegistration` struct used by the contract 15 | bytes32 public constant OPERATOR_SET_REGISTRATION_TYPEHASH = 16 | keccak256("OperatorSetRegistration(address avs,uint32[] operatorSetIds,bytes32 salt,uint256 expiry)"); 17 | 18 | /// @notice The EIP-712 typehash for the `OperatorSetMembership` struct used by the contract 19 | bytes32 public constant OPERATOR_SET_FORCE_DEREGISTRATION_TYPEHASH = 20 | keccak256("OperatorSetForceDeregistration(address avs,uint32[] operatorSetIds,bytes32 salt,uint256 expiry)"); 21 | 22 | /// @dev Index for flag that pauses operator register/deregister to avs when set. 23 | uint8 internal constant PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS = 0; 24 | 25 | /// @dev Index for flag that pauses operator register/deregister to operator sets when set. 26 | uint8 internal constant PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION = 1; 27 | 28 | // Immutables 29 | 30 | /// @notice The DelegationManager contract for EigenLayer 31 | IDelegationManager public immutable delegation; 32 | 33 | // Mutatables 34 | 35 | /// @dev Do not remove, deprecated storage. 36 | bytes32 internal __deprecated_DOMAIN_SEPARATOR; 37 | 38 | /// @notice Returns the registration status of each `operator` for a given `avs`. 39 | /// @dev This storage will be deprecated once M2-based deregistration is removed. 40 | mapping(address avs => mapping(address operator => OperatorAVSRegistrationStatus)) public avsOperatorStatus; 41 | 42 | /// @notice Returns whether a `salt` has been used by a given `operator`. 43 | mapping(address operator => mapping(bytes32 salt => bool isSpent)) public operatorSaltIsSpent; 44 | 45 | // Construction 46 | 47 | constructor( 48 | IDelegationManager _delegation 49 | ) { 50 | delegation = _delegation; 51 | } 52 | 53 | /** 54 | * @dev This empty reserved space is put in place to allow future versions to add new 55 | * variables without shifting down storage in the inheritance chain. 56 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 57 | */ 58 | uint256[47] private __gap; 59 | } 60 | -------------------------------------------------------------------------------- /src/contracts/core/StrategyManagerStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../interfaces/IStrategyManager.sol"; 5 | import "../interfaces/IStrategy.sol"; 6 | import "../interfaces/IEigenPodManager.sol"; 7 | import "../interfaces/IDelegationManager.sol"; 8 | import "../interfaces/IAVSDirectory.sol"; 9 | 10 | /** 11 | * @title Storage variables for the `StrategyManager` contract. 12 | * @author Layr Labs, Inc. 13 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 14 | * @notice This storage contract is separate from the logic to simplify the upgrade process. 15 | */ 16 | abstract contract StrategyManagerStorage is IStrategyManager { 17 | // Constants 18 | 19 | /// @notice The EIP-712 typehash for the deposit struct used by the contract 20 | bytes32 public constant DEPOSIT_TYPEHASH = 21 | keccak256("Deposit(address staker,address strategy,address token,uint256 amount,uint256 nonce,uint256 expiry)"); 22 | 23 | // maximum length of dynamic arrays in `stakerStrategyList` mapping, for sanity's sake 24 | uint8 internal constant MAX_STAKER_STRATEGY_LIST_LENGTH = 32; 25 | 26 | // index for flag that pauses deposits when set 27 | uint8 internal constant PAUSED_DEPOSITS = 0; 28 | 29 | /// @notice default address for burning slashed shares and transferring underlying tokens 30 | address public constant DEFAULT_BURN_ADDRESS = 0x00000000000000000000000000000000000E16E4; 31 | 32 | // Immutables 33 | 34 | IDelegationManager public immutable delegation; 35 | 36 | // Mutatables 37 | 38 | /// @dev Do not remove, deprecated storage. 39 | bytes32 internal __deprecated_DOMAIN_SEPARATOR; 40 | 41 | /// @notice Returns the signature `nonce` for each `signer`. 42 | mapping(address signer => uint256 nonce) public nonces; 43 | 44 | /// @notice Returns the permissioned address that can whitelist strategies. 45 | address public strategyWhitelister; 46 | 47 | /// @dev Do not remove, deprecated storage. 48 | uint256 private __deprecated_withdrawalDelayBlocks; 49 | 50 | /// @notice Returns the number of deposited `shares` for a `staker` for a given `strategy`. 51 | /// @dev All of these shares may not be withdrawable if the staker has delegated to an operator that has been slashed. 52 | mapping(address staker => mapping(IStrategy strategy => uint256 shares)) public stakerDepositShares; 53 | 54 | /// @notice Returns a list of the `strategies` that a `staker` is currently staking in. 55 | mapping(address staker => IStrategy[] strategies) public stakerStrategyList; 56 | 57 | /// @dev Do not remove, deprecated storage. 58 | mapping(bytes32 withdrawalRoot => bool pending) private __deprecated_withdrawalRootPending; 59 | 60 | /// @dev Do not remove, deprecated storage. 61 | mapping(address staker => uint256 totalQueued) private __deprecated_numWithdrawalsQueued; 62 | 63 | /// @notice Returns whether a `strategy` is `whitelisted` for deposits. 64 | mapping(IStrategy strategy => bool whitelisted) public strategyIsWhitelistedForDeposit; 65 | 66 | /// @dev Do not remove, deprecated storage. 67 | mapping(address avs => uint256 shares) private __deprecated_beaconChainETHSharesToDecrementOnWithdrawal; 68 | 69 | /// @dev Do not remove, deprecated storage. 70 | mapping(IStrategy strategy => bool) private __deprecated_thirdPartyTransfersForbidden; 71 | 72 | /// @notice Returns the amount of `shares` that have been slashed on EigenLayer but not burned yet. 73 | mapping(IStrategy strategy => uint256) public burnableShares; 74 | 75 | // Construction 76 | 77 | /** 78 | * @param _delegation The delegation contract of EigenLayer. 79 | */ 80 | constructor( 81 | IDelegationManager _delegation 82 | ) { 83 | delegation = _delegation; 84 | } 85 | 86 | /** 87 | * @dev This empty reserved space is put in place to allow future versions to add new 88 | * variables without shifting down storage in the inheritance chain. 89 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 90 | */ 91 | uint256[38] private __gap; 92 | } 93 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IAVSRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | interface IAVSRegistrar { 5 | /** 6 | * @notice Called by the AllocationManager when an operator wants to register 7 | * for one or more operator sets. This method should revert if registration 8 | * is unsuccessful. 9 | * @param operator the registering operator 10 | * @param avs the AVS the operator is registering for. This should be the same as IAVSRegistrar.avs() 11 | * @param operatorSetIds the list of operator set ids being registered for 12 | * @param data arbitrary data the operator can provide as part of registration 13 | */ 14 | function registerOperator( 15 | address operator, 16 | address avs, 17 | uint32[] calldata operatorSetIds, 18 | bytes calldata data 19 | ) external; 20 | 21 | /** 22 | * @notice Called by the AllocationManager when an operator is deregistered from 23 | * one or more operator sets. If this method reverts, it is ignored. 24 | * @param operator the deregistering operator 25 | * @param avs the AVS the operator is deregistering from. This should be the same as IAVSRegistrar.avs() 26 | * @param operatorSetIds the list of operator set ids being deregistered from 27 | */ 28 | function deregisterOperator(address operator, address avs, uint32[] calldata operatorSetIds) external; 29 | 30 | /** 31 | * @notice Returns true if the AVS is supported by the registrar 32 | * @param avs the AVS to check 33 | * @return true if the AVS is supported, false otherwise 34 | */ 35 | function supportsAVS( 36 | address avs 37 | ) external view returns (bool); 38 | } 39 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IBackingEigen.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IBackingEigen is IERC20 { 7 | /** 8 | * @notice This function allows the owner to set the allowedFrom status of an address 9 | * @param from the address whose allowedFrom status is being set 10 | * @param isAllowedFrom the new allowedFrom status 11 | */ 12 | function setAllowedFrom(address from, bool isAllowedFrom) external; 13 | 14 | /** 15 | * @notice This function allows the owner to set the allowedTo status of an address 16 | * @param to the address whose allowedTo status is being set 17 | * @param isAllowedTo the new allowedTo status 18 | */ 19 | function setAllowedTo(address to, bool isAllowedTo) external; 20 | 21 | /** 22 | * @notice Allows the owner to disable transfer restrictions 23 | */ 24 | function disableTransferRestrictions() external; 25 | 26 | /** 27 | * @notice An initializer function that sets initial values for the contract's state variables. 28 | */ 29 | function initialize( 30 | address initialOwner 31 | ) external; 32 | 33 | // @notice Allows the contract owner to modify an entry in the `isMinter` mapping. 34 | function setIsMinter(address minterAddress, bool newStatus) external; 35 | 36 | /** 37 | * @notice Allows any privileged address to mint `amount` new tokens to the address `to`. 38 | * @dev Callable only by an address that has `isMinter` set to true. 39 | */ 40 | function mint(address to, uint256 amount) external; 41 | 42 | /** 43 | * @dev Destroys `amount` tokens from the caller. 44 | * 45 | * See {ERC20-_burn}. 46 | */ 47 | function burn( 48 | uint256 amount 49 | ) external; 50 | 51 | /// @notice the address of the wrapped Eigen token EIGEN 52 | function EIGEN() external view returns (IERC20); 53 | 54 | /// @notice the timestamp after which transfer restrictions are disabled 55 | function transferRestrictionsDisabledAfter() external view returns (uint256); 56 | 57 | /** 58 | * @dev Clock used for flagging checkpoints. Has been overridden to implement timestamp based 59 | * checkpoints (and voting). 60 | */ 61 | function clock() external view returns (uint48); 62 | 63 | /** 64 | * @dev Machine-readable description of the clock as specified in EIP-6372. 65 | * Has been overridden to inform callers that this contract uses timestamps instead of block numbers, to match `clock()` 66 | */ 67 | // solhint-disable-next-line func-name-mixedcase 68 | function CLOCK_MODE() external pure returns (string memory); 69 | } 70 | -------------------------------------------------------------------------------- /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/IEigen.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.5.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IEigen is IERC20 { 7 | /** 8 | * @notice This function allows the owner to set the allowedFrom status of an address 9 | * @param from the address whose allowedFrom status is being set 10 | * @param isAllowedFrom the new allowedFrom status 11 | */ 12 | function setAllowedFrom(address from, bool isAllowedFrom) external; 13 | 14 | /** 15 | * @notice This function allows the owner to set the allowedTo status of an address 16 | * @param to the address whose allowedTo status is being set 17 | * @param isAllowedTo the new allowedTo status 18 | */ 19 | function setAllowedTo(address to, bool isAllowedTo) external; 20 | 21 | /** 22 | * @notice Allows the owner to disable transfer restrictions 23 | */ 24 | function disableTransferRestrictions() external; 25 | 26 | /** 27 | * @notice This function allows minter to mint tokens 28 | */ 29 | function mint() external; 30 | 31 | /** 32 | * @notice This function allows bEIGEN holders to wrap their tokens into Eigen 33 | */ 34 | function wrap( 35 | uint256 amount 36 | ) external; 37 | 38 | /** 39 | * @notice This function allows Eigen holders to unwrap their tokens into bEIGEN 40 | */ 41 | function unwrap( 42 | uint256 amount 43 | ) external; 44 | 45 | /** 46 | * @dev Clock used for flagging checkpoints. Has been overridden to implement timestamp based 47 | * checkpoints (and voting). 48 | */ 49 | function clock() external view returns (uint48); 50 | 51 | /** 52 | * @dev Machine-readable description of the clock as specified in EIP-6372. 53 | * Has been overridden to inform callers that this contract uses timestamps instead of block numbers, to match `clock()` 54 | */ 55 | // solhint-disable-next-line func-name-mixedcase 56 | function CLOCK_MODE() external pure returns (string memory); 57 | } 58 | -------------------------------------------------------------------------------- /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 | error OnlyUnpauser(); 11 | error InputAddressZero(); 12 | 13 | event PauserStatusChanged(address pauser, bool canPause); 14 | 15 | event UnpauserChanged(address previousUnpauser, address newUnpauser); 16 | 17 | /// @notice Mapping of addresses to whether they hold the pauser role. 18 | function isPauser( 19 | address pauser 20 | ) external view returns (bool); 21 | 22 | /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses. 23 | function unpauser() external view returns (address); 24 | } 25 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IShareManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "../libraries/SlashingLib.sol"; 6 | import "./IStrategy.sol"; 7 | 8 | /** 9 | * @title Interface for a `IShareManager` contract. 10 | * @author Layr Labs, Inc. 11 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 12 | * @notice This contract is used by the DelegationManager as a unified interface to interact with the EigenPodManager and StrategyManager 13 | */ 14 | interface IShareManager { 15 | /// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue 16 | /// @dev strategy must be beaconChainETH when talking to the EigenPodManager 17 | function removeDepositShares(address staker, IStrategy strategy, uint256 depositSharesToRemove) external; 18 | 19 | /// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue 20 | /// @dev strategy must be beaconChainETH when talking to the EigenPodManager 21 | /// @dev token is not validated; it is only emitted as an event 22 | /// @return existingDepositShares the shares the staker had before any were added 23 | /// @return addedShares the new shares added to the staker's balance 24 | function addShares( 25 | address staker, 26 | IStrategy strategy, 27 | IERC20 token, 28 | uint256 shares 29 | ) external returns (uint256, uint256); 30 | 31 | /// @notice Used by the DelegationManager to convert deposit shares to tokens and send them to a staker 32 | /// @dev strategy must be beaconChainETH when talking to the EigenPodManager 33 | /// @dev token is not validated when talking to the EigenPodManager 34 | function withdrawSharesAsTokens(address staker, IStrategy strategy, IERC20 token, uint256 shares) external; 35 | 36 | /// @notice Returns the current shares of `user` in `strategy` 37 | /// @dev strategy must be beaconChainETH when talking to the EigenPodManager 38 | /// @dev returns 0 if the user has negative shares 39 | function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 depositShares); 40 | 41 | /** 42 | * @notice Increase the amount of burnable shares for a given Strategy. This is called by the DelegationManager 43 | * when an operator is slashed in EigenLayer. 44 | * @param strategy The strategy to burn shares in. 45 | * @param addedSharesToBurn The amount of added shares to burn. 46 | * @dev This function is only called by the DelegationManager when an operator is slashed. 47 | */ 48 | function increaseBurnableShares(IStrategy strategy, uint256 addedSharesToBurn) external; 49 | } 50 | -------------------------------------------------------------------------------- /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 | error InvalidSignature(); 11 | error SignatureExpired(); 12 | 13 | // @notice Struct that bundles together a signature and an expiration time for the signature. Used primarily for stack management. 14 | struct SignatureWithExpiry { 15 | // the signature itself, formatted as a single bytes object 16 | bytes signature; 17 | // the expiration timestamp (UTC) of the signature 18 | uint256 expiry; 19 | } 20 | 21 | // @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management. 22 | struct SignatureWithSaltAndExpiry { 23 | // the signature itself, formatted as a single bytes object 24 | bytes signature; 25 | // the salt used to generate the signature 26 | bytes32 salt; 27 | // the expiration timestamp (UTC) of the signature 28 | uint256 expiry; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/contracts/interfaces/IStrategyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "./IStrategy.sol"; 7 | 8 | /** 9 | * @title Interface for the `StrategyFactory` contract. 10 | * @author Layr Labs, Inc. 11 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 12 | * @dev This may not be compatible with non-standard ERC20 tokens. Caution is warranted. 13 | */ 14 | interface IStrategyFactory { 15 | /// @dev Thrown when attempting to deploy a strategy for a blacklisted token. 16 | error BlacklistedToken(); 17 | /// @dev Thrown when attempting to deploy a strategy that already exists. 18 | error StrategyAlreadyExists(); 19 | /// @dev Thrown when attempting to blacklist a token that is already blacklisted 20 | error AlreadyBlacklisted(); 21 | 22 | event TokenBlacklisted(IERC20 token); 23 | 24 | /// @notice Upgradeable beacon which new Strategies deployed by this contract point to 25 | function strategyBeacon() external view returns (IBeacon); 26 | 27 | /// @notice Mapping token => Strategy contract for the token 28 | /// The strategies in this mapping are deployed by the StrategyFactory. 29 | /// The factory can only deploy a single strategy per token address 30 | /// These strategies MIGHT not be whitelisted in the StrategyManager, 31 | /// though deployNewStrategy does whitelist by default. 32 | /// These strategies MIGHT not be the only strategy for the underlying token 33 | /// as additional strategies can be whitelisted by the owner of the factory. 34 | function deployedStrategies( 35 | IERC20 token 36 | ) external view returns (IStrategy); 37 | 38 | /** 39 | * @notice Deploy a new strategyBeacon contract for the ERC20 token. 40 | * @param token the token to deploy a strategy for 41 | * @dev A strategy contract must not yet exist for the token. 42 | * $dev Immense caution is warranted for non-standard ERC20 tokens, particularly "reentrant" tokens 43 | * like those that conform to ERC777. 44 | */ 45 | function deployNewStrategy( 46 | IERC20 token 47 | ) external returns (IStrategy newStrategy); 48 | 49 | /** 50 | * @notice Owner-only function to pass through a call to `StrategyManager.addStrategiesToDepositWhitelist` 51 | */ 52 | function whitelistStrategies( 53 | IStrategy[] calldata strategiesToWhitelist 54 | ) external; 55 | 56 | /** 57 | * @notice Owner-only function to pass through a call to `StrategyManager.removeStrategiesFromDepositWhitelist` 58 | */ 59 | function removeStrategiesFromWhitelist( 60 | IStrategy[] calldata strategiesToRemoveFromWhitelist 61 | ) external; 62 | 63 | /// @notice Emitted when the `strategyBeacon` is changed 64 | event StrategyBeaconModified(IBeacon previousBeacon, IBeacon newBeacon); 65 | 66 | /// @notice Emitted whenever a slot is set in the `tokenStrategy` mapping 67 | event StrategySetForToken(IERC20 token, IStrategy strategy); 68 | } 69 | -------------------------------------------------------------------------------- /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( 13 | bytes32 lenum 14 | ) internal pure returns (uint64 n) { 15 | // the number needs to be stored in little-endian encoding (ie in bytes 0-8) 16 | n = uint64(uint256(lenum >> 192)); 17 | // forgefmt: disable-next-item 18 | return (n >> 56) | 19 | ((0x00FF000000000000 & n) >> 40) | 20 | ((0x0000FF0000000000 & n) >> 24) | 21 | ((0x000000FF00000000 & n) >> 8) | 22 | ((0x00000000FF000000 & n) << 8) | 23 | ((0x0000000000FF0000 & n) << 24) | 24 | ((0x000000000000FF00 & n) << 40) | 25 | ((0x00000000000000FF & n) << 56); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/contracts/libraries/OperatorSetLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | /** 5 | * @notice An operator set identified by the AVS address and an identifier 6 | * @param avs The address of the AVS this operator set belongs to 7 | * @param id The unique identifier for the operator set 8 | */ 9 | struct OperatorSet { 10 | address avs; 11 | uint32 id; 12 | } 13 | 14 | library OperatorSetLib { 15 | function key( 16 | OperatorSet memory os 17 | ) internal pure returns (bytes32) { 18 | return bytes32(abi.encodePacked(os.avs, uint96(os.id))); 19 | } 20 | 21 | function decode( 22 | bytes32 _key 23 | ) internal pure returns (OperatorSet memory) { 24 | /// forgefmt: disable-next-item 25 | return OperatorSet({ 26 | avs: address(uint160(uint256(_key) >> 96)), 27 | id: uint32(uint256(_key) & type(uint96).max) 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/contracts/mixins/PermissionControllerMixin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.0; 3 | 4 | import "../interfaces/IPermissionController.sol"; 5 | 6 | abstract contract PermissionControllerMixin { 7 | /// @dev Thrown when the caller is not allowed to call a function on behalf of an account. 8 | error InvalidPermissions(); 9 | 10 | /// @notice Pointer to the permission controller contract. 11 | IPermissionController public immutable permissionController; 12 | 13 | constructor( 14 | IPermissionController _permissionController 15 | ) { 16 | permissionController = _permissionController; 17 | } 18 | 19 | /// @notice Checks if the caller (msg.sender) can call on behalf of an account. 20 | modifier checkCanCall( 21 | address account 22 | ) { 23 | require(_checkCanCall(account), InvalidPermissions()); 24 | _; 25 | } 26 | 27 | /** 28 | * @notice Checks if the caller is allowed to call a function on behalf of an account. 29 | * @param account the account to check 30 | * @dev `msg.sender` is the caller to check that can call the function on behalf of `account`. 31 | * @dev Returns a bool, instead of reverting 32 | */ 33 | function _checkCanCall( 34 | address account 35 | ) internal returns (bool) { 36 | return permissionController.canCall(account, msg.sender, address(this), msg.sig); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/contracts/mixins/SignatureUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; 5 | 6 | import "../interfaces/ISignatureUtils.sol"; 7 | 8 | /// @title SignatureUtils 9 | /// @notice A mixin to provide EIP-712 signature validation utilities. 10 | /// @dev Domain name is hardcoded to "EigenLayer". 11 | abstract contract SignatureUtils is ISignatureUtils { 12 | using SignatureCheckerUpgradeable for address; 13 | 14 | /// CONSTANTS 15 | 16 | /// @notice The EIP-712 typehash for the contract's domain. 17 | bytes32 internal constant EIP712_DOMAIN_TYPEHASH = 18 | keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); 19 | 20 | /// @dev Returns the original chain ID from the time the contract was deployed. 21 | uint256 internal immutable _INITIAL_CHAIN_ID; 22 | 23 | /// @dev Returns the original domain separator from the time the contract was deployed. 24 | bytes32 internal immutable _INITIAL_DOMAIN_SEPARATOR; 25 | 26 | /// CONSTRUCTION 27 | 28 | constructor() { 29 | _INITIAL_CHAIN_ID = block.chainid; 30 | _INITIAL_DOMAIN_SEPARATOR = _calculateDomainSeparator(); 31 | } 32 | 33 | /// EXTERNAL FUNCTIONS 34 | 35 | /** 36 | * @notice Returns the current EIP-712 domain separator for this contract. 37 | * 38 | * @dev The domain separator will change in the event of a fork that changes the ChainID. 39 | * @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision. 40 | * for more detailed information please read EIP-712. 41 | * @dev Use `_calculateDomainSeparator` rather than using this function. 42 | */ 43 | function domainSeparator() public view virtual returns (bytes32) { 44 | /// forgefmt: disable-next-item 45 | return block.chainid == _INITIAL_CHAIN_ID 46 | // If the chain ID is the same, return the original domain separator. 47 | ? _INITIAL_DOMAIN_SEPARATOR 48 | // If the chain ID is different, return the new domain separator. 49 | : _calculateDomainSeparator(); 50 | } 51 | 52 | /// INTERNAL HELPERS 53 | 54 | /// @dev Helper for calculating the contract's domain separator. 55 | function _calculateDomainSeparator() internal view returns (bytes32) { 56 | /// forgefmt: disable-next-item 57 | return 58 | keccak256( 59 | abi.encode( 60 | EIP712_DOMAIN_TYPEHASH, 61 | keccak256(bytes("EigenLayer")), 62 | block.chainid, 63 | address(this) 64 | ) 65 | ); 66 | } 67 | 68 | /// @dev Helper for creating valid EIP-712 signable digests. 69 | function _calculateSignableDigest( 70 | bytes32 hash 71 | ) internal view returns (bytes32) { 72 | return keccak256(abi.encodePacked("\x19\x01", domainSeparator(), hash)); 73 | } 74 | 75 | /// @dev Helper for checking if a signature is valid, reverts if not valid. 76 | function _checkIsValidSignatureNow( 77 | address signer, 78 | bytes32 signableDigest, 79 | bytes memory signature, 80 | uint256 expiry 81 | ) internal view { 82 | require(expiry >= block.timestamp, SignatureExpired()); 83 | require(signer.isValidSignatureNow(signableDigest, signature), InvalidSignature()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/contracts/permissions/PauserRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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, OnlyUnpauser()); 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( 39 | address newUnpauser 40 | ) external onlyUnpauser { 41 | _setUnpauser(newUnpauser); 42 | } 43 | 44 | function _setIsPauser(address pauser, bool canPause) internal { 45 | require(pauser != address(0), InputAddressZero()); 46 | isPauser[pauser] = canPause; 47 | emit PauserStatusChanged(pauser, canPause); 48 | } 49 | 50 | function _setUnpauser( 51 | address newUnpauser 52 | ) internal { 53 | require(newUnpauser != address(0), InputAddressZero()); 54 | emit UnpauserChanged(unpauser, newUnpauser); 55 | unpauser = newUnpauser; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/contracts/permissions/PermissionControllerStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 5 | 6 | import "../interfaces/IPermissionController.sol"; 7 | 8 | abstract contract PermissionControllerStorage is IPermissionController { 9 | using EnumerableSet for EnumerableSet.Bytes32Set; 10 | using EnumerableSet for EnumerableSet.AddressSet; 11 | 12 | struct AccountPermissions { 13 | /// @notice The pending admins of the account 14 | EnumerableSet.AddressSet pendingAdmins; 15 | /// @notice The admins of the account 16 | EnumerableSet.AddressSet admins; 17 | /// @notice Mapping from an appointee to the list of encoded target & selectors 18 | mapping(address appointee => EnumerableSet.Bytes32Set) appointeePermissions; 19 | /// @notice Mapping from encoded target & selector to the list of appointees 20 | mapping(bytes32 targetSelector => EnumerableSet.AddressSet) permissionAppointees; 21 | } 22 | 23 | /// @notice Mapping from an account to its permission 24 | mapping(address account => AccountPermissions) internal _permissions; 25 | 26 | /** 27 | * @dev This empty reserved space is put in place to allow future versions to add new 28 | * variables without shifting down storage in the inheritance chain. 29 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 30 | */ 31 | uint256[49] private __gap; 32 | } 33 | -------------------------------------------------------------------------------- /src/contracts/pods/EigenPodPausingConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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 | 21 | // Deprecated 22 | // uint8 internal constant PAUSED_EIGENPODS_VERIFY_BALANCE_UPDATE = 3; 23 | 24 | // Deprecated 25 | // uint8 internal constant PAUSED_EIGENPODS_VERIFY_WITHDRAWAL = 4; 26 | 27 | /// @notice Pausability for EigenPod's "accidental transfer" withdrawal methods 28 | uint8 internal constant PAUSED_NON_PROOF_WITHDRAWALS = 5; 29 | 30 | uint8 internal constant PAUSED_START_CHECKPOINT = 6; 31 | 32 | /// @notice Index for flag that pauses the `verifyCheckpointProofs` function *of the EigenPods* when set. see EigenPod code for details. 33 | uint8 internal constant PAUSED_EIGENPODS_VERIFY_CHECKPOINT_PROOFS = 7; 34 | 35 | uint8 internal constant PAUSED_VERIFY_STALE_BALANCE = 8; 36 | } 37 | -------------------------------------------------------------------------------- /src/contracts/strategies/StrategyFactoryStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../interfaces/IStrategyFactory.sol"; 5 | 6 | /** 7 | * @title Storage for the StrategyFactory contract. 8 | * @author Layr Labs, Inc. 9 | * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service 10 | */ 11 | abstract contract StrategyFactoryStorage is IStrategyFactory { 12 | /// @notice Upgradeable beacon which new Strategies deployed by this contract point to 13 | IBeacon public strategyBeacon; 14 | 15 | /// @notice Mapping token => Strategy contract for the token 16 | /// The strategies in this mapping are deployed by the StrategyFactory. 17 | /// The factory can only deploy a single strategy per token address 18 | /// These strategies MIGHT not be whitelisted in the StrategyManager, 19 | /// though deployNewStrategy does whitelist by default. 20 | /// These strategies MIGHT not be the only strategy for the underlying token 21 | /// as additional strategies can be whitelisted by the owner of the factory. 22 | mapping(IERC20 => IStrategy) public deployedStrategies; 23 | 24 | /// @notice Mapping token => Whether or not a strategy can be deployed for the token 25 | mapping(IERC20 => bool) public isBlacklisted; 26 | 27 | /** 28 | * @dev This empty reserved space is put in place to allow future versions to add new 29 | * variables without shifting down storage in the inheritance chain. 30 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 31 | */ 32 | uint256[48] private __gap; 33 | } 34 | -------------------------------------------------------------------------------- /src/test/harnesses/DelegationManagerHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../contracts/core/DelegationManager.sol"; 5 | import "forge-std/Test.sol"; 6 | 7 | contract DelegationManagerHarness is DelegationManager { 8 | 9 | constructor( 10 | IStrategyManager _strategyManager, 11 | IEigenPodManager _eigenPodManager, 12 | IAllocationManager _allocationManager, 13 | IPauserRegistry _pauserRegistry, 14 | IPermissionController _permissionController, 15 | uint32 _MIN_WITHDRAWAL_DELAY 16 | ) 17 | DelegationManager( 18 | _strategyManager, 19 | _eigenPodManager, 20 | _allocationManager, 21 | _pauserRegistry, 22 | _permissionController, 23 | _MIN_WITHDRAWAL_DELAY 24 | ) 25 | {} 26 | 27 | function getSlashingFactor( 28 | address staker, 29 | IStrategy strategy, 30 | uint64 operatorMaxMagnitude 31 | ) external view returns (uint256) { 32 | return _getSlashingFactor(staker, strategy, operatorMaxMagnitude); 33 | } 34 | 35 | function getSlashingFactors( 36 | address staker, 37 | address operator, 38 | IStrategy[] memory strategies 39 | ) external view returns (uint256[] memory) { 40 | return _getSlashingFactors(staker, operator, strategies); 41 | } 42 | 43 | function getSlashingFactorsAtBlock( 44 | address staker, 45 | address operator, 46 | IStrategy[] memory strategies, 47 | uint32 blockNumber 48 | ) external view returns (uint256[] memory) { 49 | return _getSlashingFactorsAtBlock(staker, operator, strategies, blockNumber); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/harnesses/EigenHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../contracts/token/Eigen.sol"; 5 | 6 | contract EigenHarness is Eigen { 7 | 8 | constructor(IERC20 _bEIGEN) Eigen(_bEIGEN) { } 9 | 10 | /// expose internal mint function 11 | function mint(address to, uint256 amount) public { 12 | _mint(to, amount); 13 | } 14 | 15 | /** 16 | * @notice This function allows the owner to set the allowedFrom status of an address 17 | * @param from the address whose allowedFrom status is being set 18 | * @param isAllowedFrom the new allowedFrom status 19 | * @dev this function is callable by anoyone in the harness 20 | */ 21 | function setAllowedFromPermissionless(address from, bool isAllowedFrom) external { 22 | allowedFrom[from] = isAllowedFrom; 23 | emit SetAllowedFrom(from, isAllowedFrom); 24 | } 25 | 26 | function setTransferRestrictionsDisabledAfterToMax() external { 27 | transferRestrictionsDisabledAfter = type(uint256).max; 28 | } 29 | 30 | function transferOwnershipPermissionless(address newOwner) external { 31 | _transferOwnership(newOwner); 32 | } 33 | } -------------------------------------------------------------------------------- /src/test/harnesses/EigenPodHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../contracts/pods/EigenPod.sol"; 5 | import "forge-std/Test.sol"; 6 | 7 | contract EigenPodHarness is EigenPod { 8 | 9 | constructor( 10 | IETHPOSDeposit _ethPOS, 11 | IEigenPodManager _eigenPodManager, 12 | uint64 _GENESIS_TIME 13 | ) EigenPod( 14 | _ethPOS, 15 | _eigenPodManager, 16 | _GENESIS_TIME 17 | ) {} 18 | 19 | function getActiveValidatorCount() public view returns (uint256) { 20 | return activeValidatorCount; 21 | } 22 | 23 | function setActiveValidatorCount(uint _count) public { 24 | activeValidatorCount = _count; 25 | } 26 | 27 | function verifyWithdrawalCredentials( 28 | bytes32 beaconStateRoot, 29 | uint40 validatorIndex, 30 | bytes calldata validatorFieldsProof, 31 | bytes32[] calldata validatorFields 32 | ) public returns (uint256) { 33 | return _verifyWithdrawalCredentials( 34 | beaconStateRoot, 35 | validatorIndex, 36 | validatorFieldsProof, 37 | validatorFields 38 | ); 39 | } 40 | 41 | function setValidatorStatus(bytes32 pkhash, VALIDATOR_STATUS status) public { 42 | _validatorPubkeyHashToInfo[pkhash].status = status; 43 | } 44 | 45 | function setValidatorRestakedBalance(bytes32 pkhash, uint64 restakedBalanceGwei) public { 46 | _validatorPubkeyHashToInfo[pkhash].restakedBalanceGwei = restakedBalanceGwei; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/harnesses/EigenPodManagerWrapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../contracts/pods/EigenPodManager.sol"; 5 | 6 | ///@notice This contract exposes a manual setter for podShares in order to initialize podShares as negative 7 | contract EigenPodManagerWrapper is EigenPodManager { 8 | constructor( 9 | IETHPOSDeposit _ethPOS, 10 | IBeacon _eigenPodBeacon, 11 | IDelegationManager _delegationManager, 12 | IPauserRegistry _pauserRegistry 13 | ) EigenPodManager(_ethPOS, _eigenPodBeacon, _delegationManager, _pauserRegistry) {} 14 | 15 | function setPodOwnerShares(address owner, IEigenPod pod) external { 16 | ownerToPod[owner] = pod; 17 | } 18 | 19 | function setPodOwnerShares(address owner, int256 shares) external { 20 | podOwnerDepositShares[owner] = shares; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/harnesses/PausableHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "../../contracts/permissions/Pausable.sol"; 5 | 6 | // wrapper around the Pausable contract that exposes the internal `_setPausedStatus` function. 7 | contract PausableHarness is Pausable { 8 | constructor(IPauserRegistry _pauserRegistry) Pausable(_pauserRegistry) {} 9 | 10 | function initializePauser(uint256 initPausedStatus) external { 11 | _setPausedStatus(initPausedStatus); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/integration/TimeMachine.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "forge-std/Test.sol"; 5 | import "src/test/utils/Logger.t.sol"; 6 | 7 | contract TimeMachine is Test, Logger { 8 | uint256[] public snapshots; 9 | 10 | function NAME() public view virtual override returns (string memory) { 11 | return "TimeMachine"; 12 | } 13 | 14 | /// ----------------------------------------------------------------------- 15 | /// Setters 16 | /// ----------------------------------------------------------------------- 17 | 18 | function createSnapshot() public returns (uint256 snapshot) { 19 | snapshots.push(snapshot = cheats.snapshotState()); 20 | print.method("createSnapshot", cheats.toString(snapshot)); 21 | } 22 | 23 | function travelToLast() public returns (uint256 currentSnapshot) { 24 | // Safety check to make sure createSnapshot is called before attempting 25 | // to warp so we don't accidentally prevent our own births. 26 | assertTrue(pastExists(), "Global.warpToPast: invalid usage, past does not exist"); 27 | uint256 last = lastSnapshot(); 28 | print.method("travelToLast", cheats.toString(last)); 29 | currentSnapshot = createSnapshot(); 30 | cheats.revertToState(last); 31 | } 32 | 33 | function travel( 34 | uint256 snapshot 35 | ) public { 36 | print.method("travel", cheats.toString(snapshot)); 37 | cheats.revertToState(snapshot); 38 | } 39 | 40 | /// ----------------------------------------------------------------------- 41 | /// Getters 42 | /// ----------------------------------------------------------------------- 43 | 44 | function lastSnapshot() public view returns (uint256) { 45 | return snapshots[snapshots.length - 1]; 46 | } 47 | 48 | function pastExists() public view returns (bool) { 49 | return snapshots.length != 0; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/integration/deprecatedInterfaces/mainnet/IDelayedWithdrawalRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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/EIP_4788_Oracle_Mock.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | contract EIP_4788_Oracle_Mock { 5 | 6 | mapping(uint => bytes32) blockRoots; 7 | 8 | uint constant HISTORY_BUFFER_LENGTH = 8191; 9 | 10 | fallback() external { 11 | require(msg.data.length == 32, "4788OracleMock.fallback: malformed msg.data"); 12 | 13 | uint timestamp = abi.decode(msg.data, (uint)); 14 | require(timestamp != 0, "4788OracleMock.fallback: timestamp is 0"); 15 | 16 | bytes32 blockRoot = blockRoots[timestamp]; 17 | require(blockRoot != 0, "4788OracleMock.fallback: no block root found. DID YOU USE CHEATS.WARP?"); 18 | 19 | assembly { 20 | mstore(0, blockRoot) 21 | return(0, 32) 22 | } 23 | } 24 | 25 | function timestampToBlockRoot(uint timestamp) public view returns (bytes32) { 26 | return blockRoots[uint64(timestamp)]; 27 | } 28 | 29 | function setBlockRoot(uint64 timestamp, bytes32 blockRoot) public { 30 | blockRoots[timestamp] = blockRoot; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/integration/tests/Deposit_Register_QueueWithdrawal_Complete.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "src/test/integration/users/User.t.sol"; 5 | import "src/test/integration/IntegrationChecks.t.sol"; 6 | 7 | contract Integration_Deposit_Register_QueueWithdrawal_Complete is IntegrationCheckUtils { 8 | function testFuzz_deposit_registerOperator_queueWithdrawal_completeAsShares(uint24 _random) public { 9 | // Configure the random parameters for the test 10 | _configRand({ 11 | _randomSeed: _random, 12 | _assetTypes: HOLDS_LST | HOLDS_ETH | HOLDS_ALL, 13 | _userTypes: DEFAULT | ALT_METHODS 14 | }); 15 | 16 | // Create a staker with a nonzero balance and corresponding strategies 17 | (User staker, IStrategy[] memory strategies, uint[] memory tokenBalances) = _newRandomStaker(); 18 | // Upgrade contracts if forkType is not local 19 | _upgradeEigenLayerContracts(); 20 | 21 | // 1. Staker deposits into strategy 22 | staker.depositIntoEigenlayer(strategies, tokenBalances); 23 | uint[] memory shares = _calculateExpectedShares(strategies, tokenBalances); 24 | check_Deposit_State(staker, strategies, shares); 25 | 26 | // 2. Staker registers as an operator 27 | staker.registerAsOperator(); 28 | assertTrue(delegationManager.isOperator(address(staker)), "Staker should be registered as an operator"); 29 | 30 | // 3. Queue Withdrawal 31 | IDelegationManagerTypes.Withdrawal[] memory withdrawals = staker.queueWithdrawals(strategies, shares); 32 | bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); 33 | check_QueuedWithdrawal_State(staker, staker, strategies, shares, withdrawals, withdrawalRoots); 34 | 35 | // 4. Complete Queued Withdrawal as Shares 36 | _rollBlocksForCompleteWithdrawals(withdrawals); 37 | for (uint i = 0; i < withdrawals.length; i++) { 38 | staker.completeWithdrawalAsShares(withdrawals[i]); 39 | check_Withdrawal_AsShares_State(staker, staker, withdrawals[i], strategies, shares); 40 | } 41 | } 42 | 43 | function testFuzz_deposit_registerOperator_queueWithdrawal_completeAsTokens(uint24 _random) public { 44 | // Configure the random parameters for the test 45 | _configRand({ 46 | _randomSeed: _random, 47 | _assetTypes: HOLDS_LST | HOLDS_ETH | HOLDS_ALL, 48 | _userTypes: DEFAULT | ALT_METHODS 49 | }); 50 | 51 | // Create a staker with a nonzero balance and corresponding strategies 52 | (User staker, IStrategy[] memory strategies, uint[] memory tokenBalances) = _newRandomStaker(); 53 | // Upgrade contracts if forkType is not local 54 | _upgradeEigenLayerContracts(); 55 | 56 | // 1. Staker deposits into strategy 57 | staker.depositIntoEigenlayer(strategies, tokenBalances); 58 | uint[] memory shares = _calculateExpectedShares(strategies, tokenBalances); 59 | check_Deposit_State(staker, strategies, shares); 60 | 61 | // 2. Staker registers as an operator 62 | staker.registerAsOperator(); 63 | assertTrue(delegationManager.isOperator(address(staker)), "Staker should be registered as an operator"); 64 | 65 | // 3. Queue Withdrawal 66 | IDelegationManagerTypes.Withdrawal[] memory withdrawals = staker.queueWithdrawals(strategies, shares); 67 | bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); 68 | check_QueuedWithdrawal_State(staker, staker, strategies, shares, withdrawals, withdrawalRoots); 69 | 70 | // 4. Complete Queued Withdrawal as Tokens 71 | _rollBlocksForCompleteWithdrawals(withdrawals); 72 | for (uint i = 0; i < withdrawals.length; i++) { 73 | IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]); 74 | uint[] memory expectedTokens = _calculateExpectedTokens(strategies, shares); 75 | 76 | check_Withdrawal_AsTokens_State(staker, staker, withdrawals[i], strategies, shares, tokens, expectedTokens); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/integration/tests/Upgrade_Setup.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "src/test/integration/IntegrationChecks.t.sol"; 5 | 6 | contract IntegrationMainnetFork_UpgradeSetup is IntegrationCheckUtils { 7 | 8 | // /// @notice Test upgrade setup is correct 9 | // /// forge-config: default.fuzz.runs = 1 10 | // function test_mainnet_upgrade_setup(uint24 _random) public { 11 | // _configRand({ 12 | // _randomSeed: _random, 13 | // _assetTypes: HOLDS_LST | HOLDS_ETH | HOLDS_ALL, 14 | // _userTypes: DEFAULT | ALT_METHODS 15 | // }); 16 | 17 | // // 1. Check proper state pre-upgrade 18 | // _verifyContractPointers(); 19 | // _verifyImplementations(); 20 | // _verifyContractsInitialized(false); 21 | // _verifyInitializationParams(); 22 | 23 | // // 2. Upgrade mainnet contracts 24 | // _upgradeEigenLayerContracts(); 25 | // _parseInitialDeploymentParams("script/configs/mainnet/M2_mainnet_upgrade.config.json"); 26 | 27 | // // 2. Verify upgrade setup 28 | // _verifyContractPointers(); 29 | // _verifyImplementations(); 30 | // _verifyContractsInitialized(false); 31 | // _verifyInitializationParams(); 32 | // } 33 | 34 | // /// @notice Test upgrade setup is correct 35 | // /// forge-config: default.fuzz.runs = 1 36 | // function test_holesky_upgrade_setup(uint24 _random) public { 37 | // _configRand({ 38 | // _randomSeed: _random, 39 | // _assetTypes: HOLDS_LST | HOLDS_ETH | HOLDS_ALL, 40 | // _userTypes: DEFAULT | ALT_METHODS, 41 | // _forkTypes: HOLESKY 42 | // }); 43 | 44 | // // // 1. Check proper state pre-upgrade 45 | // // _verifyContractPointers(); 46 | // // _verifyImplementations(); 47 | // // _verifyContractsInitialized(true); 48 | // // _verifyInitializationParams(); 49 | 50 | // // 2. Upgrade holesky contracts 51 | // _upgradeEigenLayerContracts(); 52 | // _parseInitialDeploymentParams("script/configs/holesky/M2_deploy_from_scratch.holesky.config.json"); 53 | 54 | // // 3. Verify upgrade setup 55 | // _verifyContractPointers(); 56 | // _verifyImplementations(); 57 | // _verifyContractsInitialized(true); 58 | // _verifyInitializationParams(); 59 | // } 60 | 61 | /// @notice Ensure contracts point at each other correctly via constructors 62 | /// override to remove ethPOSDeposit contract check 63 | function _verifyContractPointers() internal virtual override view { 64 | // AVSDirectory 65 | require( 66 | avsDirectory.delegation() == delegationManager, 67 | "avsDirectory: delegationManager address not set correctly" 68 | ); 69 | // DelegationManager 70 | require( 71 | delegationManager.strategyManager() == strategyManager, 72 | "delegationManager: strategyManager address not set correctly" 73 | ); 74 | require( 75 | delegationManager.eigenPodManager() == eigenPodManager, 76 | "delegationManager: eigenPodManager address not set correctly" 77 | ); 78 | // StrategyManager 79 | require( 80 | strategyManager.delegation() == delegationManager, 81 | "strategyManager: delegationManager address not set correctly" 82 | ); 83 | // EPM 84 | require( 85 | eigenPodManager.eigenPodBeacon() == eigenPodBeacon, 86 | "eigenPodManager: eigenPodBeacon contract address not set correctly" 87 | ); 88 | require( 89 | eigenPodManager.delegationManager() == delegationManager, 90 | "eigenPodManager: delegationManager contract address not set correctly" 91 | ); 92 | } 93 | } -------------------------------------------------------------------------------- /src/test/integration/tests/eigenpod/EigenPod_Slashing_Migration.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "src/test/integration/IntegrationChecks.t.sol"; 5 | import "src/test/integration/users/User.t.sol"; 6 | 7 | contract Integration_EigenPod_Slashing_Migration is IntegrationCheckUtils, EigenPodPausingConstants { 8 | modifier r(uint24 _rand) { 9 | _configRand({ 10 | _randomSeed: _rand, 11 | _assetTypes: HOLDS_ETH, 12 | _userTypes: DEFAULT 13 | }); 14 | 15 | _; 16 | } 17 | 18 | /** 19 | * 1. Verify validators' withdrawal credentials 20 | * -- earn rewards on beacon chain (withdrawn to pod) 21 | * 2. Start a checkpoint 22 | * 3. Pause starting checkpoints 23 | * 4. Complete in progress checkpoint 24 | * 5. Upgrade EigenPod contracts 25 | * 6. Exit subset of Validators 26 | */ 27 | function test_upgrade_eigenpod_migration(uint24 _rand) public r(_rand) { 28 | // Only run this test as a fork test 29 | if (forkType == LOCAL) { 30 | return; 31 | } 32 | 33 | // Initialize state 34 | (User staker, ,) = _newRandomStaker(); 35 | 36 | (uint40[] memory validators, ) = staker.startValidators(); 37 | beaconChain.advanceEpoch_NoRewards(); 38 | 39 | // 1. Verify validators' withdrawal credentials 40 | staker.verifyWithdrawalCredentials(validators); 41 | 42 | // Advance epoch, generating consensus rewards and withdrawing anything over 32 ETH 43 | beaconChain.advanceEpoch(); 44 | uint64 expectedWithdrawnGwei = uint64(validators.length) * beaconChain.CONSENSUS_REWARD_AMOUNT_GWEI(); 45 | 46 | // 2. Start a checkpoint 47 | staker.startCheckpoint(); 48 | 49 | // 3. Pause checkpoint starting 50 | cheats.prank(pauserMultisig); 51 | eigenPodManager.pause(2 ** PAUSED_START_CHECKPOINT); 52 | 53 | cheats.expectRevert("EigenPod.onlyWhenNotPaused: index is paused in EigenPodManager"); 54 | staker.startCheckpoint(); 55 | 56 | // 4. Complete in progress checkpoint 57 | staker.completeCheckpoint(); 58 | check_CompleteCheckpoint_WithPodBalance_State(staker, expectedWithdrawnGwei); 59 | 60 | // 5. Upgrade Contracts for slashing 61 | _upgradeEigenLayerContracts(); 62 | 63 | // 6. Exit validators 64 | // Fully exit one or more validators and advance epoch without generating rewards 65 | uint40[] memory subset = _choose(validators); 66 | uint64 exitedBalanceGwei = staker.exitValidators(subset); 67 | beaconChain.advanceEpoch_NoRewards(); 68 | 69 | staker.startCheckpoint(); 70 | check_StartCheckpoint_WithPodBalance_State(staker, exitedBalanceGwei); 71 | 72 | staker.completeCheckpoint(); 73 | check_CompleteCheckpoint_WithExits_State(staker, subset, exitedBalanceGwei); 74 | } 75 | } -------------------------------------------------------------------------------- /src/test/mocks/AVSDirectoryMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.9; 3 | 4 | import "forge-std/Test.sol"; 5 | import "src/contracts/interfaces/IAVSDirectory.sol"; 6 | import "src/contracts/libraries/OperatorSetLib.sol"; 7 | 8 | contract AVSDirectoryMock is Test { 9 | receive() external payable {} 10 | fallback() external payable {} 11 | } -------------------------------------------------------------------------------- /src/test/mocks/AllocationManagerMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.9; 3 | 4 | import "forge-std/Test.sol"; 5 | import "src/contracts/interfaces/IStrategy.sol"; 6 | import "src/contracts/libraries/Snapshots.sol"; 7 | import "src/contracts/libraries/OperatorSetLib.sol"; 8 | 9 | contract AllocationManagerMock is Test { 10 | using Snapshots for Snapshots.DefaultWadHistory; 11 | using OperatorSetLib for OperatorSet; 12 | 13 | receive() external payable {} 14 | fallback() external payable {} 15 | 16 | mapping(bytes32 operatorSetKey => bool) public _isOperatorSet; 17 | mapping(address avs => uint256) public getOperatorSetCount; 18 | mapping(address => mapping(IStrategy => Snapshots.DefaultWadHistory)) internal _maxMagnitudeHistory; 19 | 20 | function setIsOperatorSet(OperatorSet memory operatorSet, bool boolean) external { 21 | _isOperatorSet[operatorSet.key()] = boolean; 22 | } 23 | 24 | function isOperatorSet(OperatorSet memory operatorSet) external view returns (bool) { 25 | return _isOperatorSet[operatorSet.key()]; 26 | } 27 | 28 | function setMaxMagnitudes( 29 | address operator, 30 | IStrategy[] calldata strategies, 31 | uint64[] calldata maxMagnitudes 32 | ) external { 33 | for (uint256 i = 0; i < strategies.length; ++i) { 34 | setMaxMagnitude(operator, strategies[i], maxMagnitudes[i]); 35 | } 36 | } 37 | 38 | function setMaxMagnitude( 39 | address operator, 40 | IStrategy strategy, 41 | uint64 maxMagnitude 42 | ) public { 43 | _maxMagnitudeHistory[operator][strategy].push({ 44 | key: uint32(block.number), 45 | value: maxMagnitude 46 | }); 47 | } 48 | 49 | function getMaxMagnitude( 50 | address operator, 51 | IStrategy strategy 52 | ) external view returns (uint64) { 53 | return _maxMagnitudeHistory[operator][strategy].latest(); 54 | } 55 | 56 | function getMaxMagnitudes( 57 | address operator, 58 | IStrategy[] calldata strategies 59 | ) external view returns (uint64[] memory) { 60 | uint64[] memory maxMagnitudes = new uint64[](strategies.length); 61 | 62 | for (uint256 i = 0; i < strategies.length; ++i) { 63 | maxMagnitudes[i] = _maxMagnitudeHistory[operator][strategies[i]].latest(); 64 | } 65 | 66 | return maxMagnitudes; 67 | } 68 | 69 | function getMaxMagnitudesAtBlock( 70 | address operator, 71 | IStrategy[] calldata strategies, 72 | uint32 blockNumber 73 | ) external view returns (uint64[] memory) { 74 | uint64[] memory maxMagnitudes = new uint64[](strategies.length); 75 | 76 | for (uint256 i = 0; i < strategies.length; ++i) { 77 | maxMagnitudes[i] = _maxMagnitudeHistory[operator][strategies[i]].upperLookup({ 78 | key: blockNumber 79 | }); 80 | } 81 | 82 | return maxMagnitudes; 83 | } 84 | 85 | function setAVSSetCount(address avs, uint256 numSets) external { 86 | getOperatorSetCount[avs] = numSets; 87 | } 88 | } -------------------------------------------------------------------------------- /src/test/mocks/Dummy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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.27; 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.27; 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/IStrategy.sol"; 6 | import "../../contracts/permissions/Pausable.sol"; 7 | 8 | contract EigenPodManagerMock is Test, Pausable { 9 | receive() external payable {} 10 | fallback() external payable {} 11 | 12 | mapping(address => int256) public podOwnerDepositShares; 13 | 14 | mapping(address => uint256) public podOwnerSharesWithdrawn; 15 | 16 | struct BeaconChainSlashingFactor { 17 | bool isSet; 18 | uint64 slashingFactor; 19 | } 20 | 21 | mapping(address => BeaconChainSlashingFactor) _beaconChainSlashingFactor; 22 | 23 | constructor(IPauserRegistry _pauserRegistry) Pausable(_pauserRegistry) { 24 | _setPausedStatus(0); 25 | } 26 | 27 | function podOwnerShares(address podOwner) external view returns (int256) { 28 | return podOwnerDepositShares[podOwner]; 29 | } 30 | 31 | function stakerDepositShares(address user, address) public view returns (uint256 depositShares) { 32 | return podOwnerDepositShares[user] < 0 ? 0 : uint256(podOwnerDepositShares[user]); 33 | } 34 | 35 | function setPodOwnerShares(address podOwner, int256 shares) external { 36 | podOwnerDepositShares[podOwner] = shares; 37 | } 38 | 39 | function addShares( 40 | address podOwner, 41 | IStrategy, 42 | IERC20, 43 | uint256 shares 44 | ) external returns (uint256, uint256) { 45 | uint256 existingDepositShares = uint256(podOwnerDepositShares[podOwner]); 46 | podOwnerDepositShares[podOwner] += int256(shares); 47 | return (existingDepositShares, shares); 48 | } 49 | 50 | function removeDepositShares( 51 | address podOwner, 52 | IStrategy, // strategy 53 | uint256 shares 54 | ) external { 55 | podOwnerDepositShares[podOwner] -= int256(shares); 56 | } 57 | 58 | function denebForkTimestamp() external pure returns (uint64) { 59 | return type(uint64).max; 60 | } 61 | 62 | function withdrawSharesAsTokens(address podOwner, address /** strategy */, address /** token */, uint256 shares) external { 63 | podOwnerSharesWithdrawn[podOwner] += shares; 64 | } 65 | 66 | function setBeaconChainSlashingFactor(address staker, uint64 bcsf) external { 67 | _beaconChainSlashingFactor[staker] = BeaconChainSlashingFactor({ 68 | isSet: true, 69 | slashingFactor: bcsf 70 | }); 71 | } 72 | 73 | function beaconChainSlashingFactor(address staker) external view returns (uint64) { 74 | BeaconChainSlashingFactor memory bsf = _beaconChainSlashingFactor[staker]; 75 | return bsf.isSet ? bsf.slashingFactor : WAD; 76 | } 77 | } -------------------------------------------------------------------------------- /src/test/mocks/EmptyContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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/MockAVSRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | contract MockAVSRegistrar { 5 | function supportsAVS( 6 | address /*avs*/ 7 | ) external pure returns (bool) { 8 | return true; 9 | } 10 | 11 | fallback() external {} 12 | } 13 | -------------------------------------------------------------------------------- /src/test/mocks/MockDecimals.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | contract MockDecimals { 5 | function decimals() public pure returns (uint8) { 6 | return 18; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/mocks/OwnableMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 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(VM_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 | fallback() external { 6 | revert("Reverter: I am a contract that always reverts"); 7 | } 8 | } 9 | 10 | contract ReverterWithDecimals is Reverter { 11 | function decimals() external pure returns (uint8) { 12 | return 18; 13 | } 14 | } -------------------------------------------------------------------------------- /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/test-data/rewardsCoordinator/processClaimProofs_MaxEarnerAndLeafIndices.json: -------------------------------------------------------------------------------- 1 | { 2 | "Root": "0x37550707c80f3d8907c467999730e52127ab89be3f17a5017a3f1ffb73a1445f", 3 | "RootIndex": 0, 4 | "EarnerIndex": 7, 5 | "EarnerTreeProof": "0x4bf5e16eaabbc36964f1e1639808669420f55d60e51adb7e9695b77145c479fd6777be59643947bb24d78e69d6605bf369c515b479f3a8967dd68a97c5bb4a4a262b28002eeb6cbbffb7e79e5741bf2be189a6073440a62fabcd8af4dbda94e3", 6 | "EarnerLeaf": { 7 | "Earner": "0x25a1b7322f9796b26a4bec125913b34c292b28d6", 8 | "EarnerTokenRoot": "0xf8e7e20b32aae1d818dcb593b98982841e9a0ed12c161ad603e3ee3948746cba" 9 | }, 10 | "LeafIndices": [ 11 | 7 12 | ], 13 | "TokenTreeProofs": [ 14 | "0x3cd04e8fc6f23812c570fe12292a30bb9e105e00f5913ac4b4938f23e65d8d10e6b1403d58c9d5450952e7d96c81305dad9fb966e8a27d3a42058e3958a0d30033148e91b455542d05deb81b8305b672e742cd3145f7022a0089bad2e6af9173" 15 | ], 16 | "TokenLeaves": [ 17 | { 18 | "Token": "0x7fbfdd1dfd80730385aee232cc9f79b8ae12a654", 19 | "CumulativeEarnings": 3000000000000000000 20 | } 21 | ], 22 | "TokenTreeProofsNum": 1, 23 | "TokenLeavesNum": 1 24 | } -------------------------------------------------------------------------------- /src/test/test-data/rewardsCoordinator/processClaimProofs_Root1.json: -------------------------------------------------------------------------------- 1 | { 2 | "Root": "0xc5d6bb1073f9040366851b5971493165893558f1cdc0b0046b6703baa85cddfc", 3 | "RootIndex": 0, 4 | "EarnerIndex": 3, 5 | "EarnerTreeProof": "0x04da796e892869089dfdaae7269c8eb12548f6aa3774a747322b65c42d1ca0050d38d6d5e7e9e65ba24c275fcc0f0c377d8fb6ed089770d849c39a1829d1edf27e78a529aa8f867a3777f97541a23fb1844d6ae24c3b8ca1cc981510e5d08bda", 6 | "EarnerLeaf": { 7 | "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", 8 | "EarnerTokenRoot": "0xa81e82a39e6da197c3c83b8b1343eb7e8a969db52d4bfc424fd04d60350d76e3" 9 | }, 10 | "LeafIndices": [ 11 | 0, 12 | 1, 13 | 2, 14 | 3, 15 | 4, 16 | 5 17 | ], 18 | "TokenTreeProofs": [ 19 | "0x6166682a9a29283a51a1c1575de82334227cc45b1ce686973039a44eb9a6b008e3e64bd80597510ac0b737fc645f15801cc3835b279412e1a09ba66d50c2aa82e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", 20 | "0x167f18d55815451f979244946b7eb2ce019c323a9e02fba1b2e05e19a27b91b5e3e64bd80597510ac0b737fc645f15801cc3835b279412e1a09ba66d50c2aa82e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", 21 | "0x2faa07574e263370227b938c72ca18ae40edf215067ce325ebfc36f11b1f19484923b477146618e0f36993536e7bbec8ef5346613df2fb9d53caf8d9365b4c68e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", 22 | "0x4dccc729db7b3ad40fc9acfe257d45196427285382332a4bc6704e1ae42785474923b477146618e0f36993536e7bbec8ef5346613df2fb9d53caf8d9365b4c68e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", 23 | "0x1e9348730aee854752d72b32f4eed96ad80093807b53f3a07068536ce96d0e9aad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb51ab784a49394ced1b194ec2cac2163a6a5a0108e31a2510f82162f3d41b79962", 24 | "0x7a570fedf4656f3240f44fb4771c946ea688c554f82e204778b792013b29ded3ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb51ab784a49394ced1b194ec2cac2163a6a5a0108e31a2510f82162f3d41b79962" 25 | ], 26 | "TokenLeaves": [ 27 | { 28 | "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", 29 | "CumulativeEarnings": 1000000000000000000 30 | }, 31 | { 32 | "Token": "0x11a4b85eab283c98d27c8ae64469224d55ed1894", 33 | "CumulativeEarnings": 400000000000000000 34 | }, 35 | { 36 | "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", 37 | "CumulativeEarnings": 30000000000000000000 38 | }, 39 | { 40 | "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", 41 | "CumulativeEarnings": 250000000000 42 | }, 43 | { 44 | "Token": "0xd275b23e0a5b68ae251b0dc4c81104cba36e7cd6", 45 | "CumulativeEarnings": 884300000000000000 46 | }, 47 | { 48 | "Token": "0xec562acb9e470de27dca2495950660fa9fbd85f8", 49 | "CumulativeEarnings": 42000000000000 50 | } 51 | ], 52 | "TokenTreeProofsNum": 6, 53 | "TokenLeavesNum": 6 54 | } -------------------------------------------------------------------------------- /src/test/test-data/rewardsCoordinator/processClaimProofs_Root2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Root": "0x4d58d15093f392176c29504b94be56fb4969cf100083b6e8d3c79373f2d25974", 3 | "RootIndex": 0, 4 | "EarnerIndex": 3, 5 | "EarnerTreeProof": "0xa9ed93ecba6058bc5cfab91319b40d344e7c829fb40eb26208e2898cb90071ed3bcd0aa5a1fbb1782053d35aaec2c6203ff53f6d6493100bafbf9506c7a3edf2bbcc90923e3f236d88ec22330aa91f3f50c425bc56ad3ff9707c72dcf853e206", 6 | "EarnerLeaf": { 7 | "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", 8 | "EarnerTokenRoot": "0x6eeac100b8cd705b92cda3015844005d918e118a3c7ba20046b6523cdc203d48" 9 | }, 10 | "LeafIndices": [ 11 | 0, 12 | 1, 13 | 2, 14 | 3, 15 | 4, 16 | 5 17 | ], 18 | "TokenTreeProofs": [ 19 | "0x57898ab69c4024d674e8c18eea33fef5cd76c5b01e0f93ef168c602298f7b27ee2b46f44bc74268bc383609078e0217a0ea21d6a658975171f5233c1cd3133c0797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", 20 | "0x97a04c80233ddc54eccd67a57f442c63c8f741079f41d95dd6242e275dcbe871e2b46f44bc74268bc383609078e0217a0ea21d6a658975171f5233c1cd3133c0797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", 21 | "0xb0f239391b892c4b6f4ce0fea0a15de171f977f6798713e540b3493b93b557ffb9cf5ff8530c7a7932e4349c3c4d0172466b77e791a4e8bb1523fdb57c1fe96a797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", 22 | "0x71a2384092132151928d8a13f599e52ad0aebe5587aa1cac9199d782c08847e2b9cf5ff8530c7a7932e4349c3c4d0172466b77e791a4e8bb1523fdb57c1fe96a797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", 23 | "0xf8b6a59d2da40bb3c9a9fed1f77336c45e3550f34b8bcb76fa64adcf6d9a409dad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb508231e83c23fa7d9b9a2820a400601f21f946161a3817010724592cf53cf4ad9", 24 | "0x87fa7a4daeedebd0069679c39d095a21293b412f6f9fdae3398b454160703e3bad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb508231e83c23fa7d9b9a2820a400601f21f946161a3817010724592cf53cf4ad9" 25 | ], 26 | "TokenLeaves": [ 27 | { 28 | "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", 29 | "CumulativeEarnings": 1630000000000000000 30 | }, 31 | { 32 | "Token": "0x11a4b85eab283c98d27c8ae64469224d55ed1894", 33 | "CumulativeEarnings": 780000000000000000 34 | }, 35 | { 36 | "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", 37 | "CumulativeEarnings": 37800000000000000000 38 | }, 39 | { 40 | "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", 41 | "CumulativeEarnings": 5050000000000 42 | }, 43 | { 44 | "Token": "0xd275b23e0a5b68ae251b0dc4c81104cba36e7cd6", 45 | "CumulativeEarnings": 2284300000000000000 46 | }, 47 | { 48 | "Token": "0xec562acb9e470de27dca2495950660fa9fbd85f8", 49 | "CumulativeEarnings": 886400000000000 50 | } 51 | ], 52 | "TokenTreeProofsNum": 6, 53 | "TokenLeavesNum": 6 54 | } -------------------------------------------------------------------------------- /src/test/test-data/rewardsCoordinator/processClaimProofs_Root3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Root": "0x53d76498642862250c1caa8b14df55642d977200467a3dfce62e6da30b4820c6", 3 | "RootIndex": 0, 4 | "EarnerIndex": 3, 5 | "EarnerTreeProof": "0xecd8c0e6d2d221742f8025acd6f1f0a5ff8482fe8f1cb135439e346df6fd56acce4f8a04a8bcb37dcbc11cb6984ba73cfa4da51dd037f7be27c257cf0605673b38d2f9a729da60cd7c3c2b7ff53bebfdef61de0871518eb36654368ac584b6b8", 6 | "EarnerLeaf": { 7 | "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", 8 | "EarnerTokenRoot": "0x36c11c299ad4a8b95a795d6afc1f5f958b9d1a1ea5cc13ea2fc59b6ccd4b6ee4" 9 | }, 10 | "LeafIndices": [ 11 | 0, 12 | 1, 13 | 2, 14 | 3, 15 | 4, 16 | 5 17 | ], 18 | "TokenTreeProofs": [ 19 | "0xfcf8546a323ba4d4bcfbdb5930b3bede1e93ec4bdd8d1372ed84db3126df143fb306484eeaece55c5cd8db0ce3f3a77f4aa6fa5150b95db06b9c1cec4825aae157a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", 20 | "0xde4e83fd8b6b5e44f72fd511cd1a9ce6704f16378e5ca20c15f4d52efe27aa57b306484eeaece55c5cd8db0ce3f3a77f4aa6fa5150b95db06b9c1cec4825aae157a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", 21 | "0x67f846894f21973d7db36e6b363141b291b2f0f57bfc4b9a74b4f5335c0d068a17aac423247fab5821f643830b49570dfdecd0f30a9ae24ff5fd59bd4e9e8a4e57a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", 22 | "0xc27217da67ce7b0418f5c8bef4ceeefe04c5561ddd73ab9750798d8f981d981d17aac423247fab5821f643830b49570dfdecd0f30a9ae24ff5fd59bd4e9e8a4e57a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", 23 | "0xd084816ad70c189dbfa8be4e6b1f912edb6fe3b39ba1c19c4ed16d91b84f903dad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb550d72c280c9bd978b5ef45c3647daa2ab564342c473cb977f35b053706ed19d1", 24 | "0x2bec75e7737677b586943a2292393137cad6617ac248d249d220afb3bf31f4dcad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb550d72c280c9bd978b5ef45c3647daa2ab564342c473cb977f35b053706ed19d1" 25 | ], 26 | "TokenLeaves": [ 27 | { 28 | "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", 29 | "CumulativeEarnings": 3500000000000000000 30 | }, 31 | { 32 | "Token": "0x11a4b85eab283c98d27c8ae64469224d55ed1894", 33 | "CumulativeEarnings": 1240000000000000000 34 | }, 35 | { 36 | "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", 37 | "CumulativeEarnings": 56800000000000000000 38 | }, 39 | { 40 | "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", 41 | "CumulativeEarnings": 8900000000000 42 | }, 43 | { 44 | "Token": "0xd275b23e0a5b68ae251b0dc4c81104cba36e7cd6", 45 | "CumulativeEarnings": 4343000000000000000 46 | }, 47 | { 48 | "Token": "0xec562acb9e470de27dca2495950660fa9fbd85f8", 49 | "CumulativeEarnings": 1064000000000000 50 | } 51 | ], 52 | "TokenTreeProofsNum": 6, 53 | "TokenLeavesNum": 6 54 | } -------------------------------------------------------------------------------- /src/test/test-data/rewardsCoordinator/processClaimProofs_SingleEarnerLeaf.json: -------------------------------------------------------------------------------- 1 | { 2 | "Root": "0xd4aa4d1bdb95eb78f061238d587609407301a05bd952334ad6b5e8ca60bb347e", 3 | "RootIndex": 0, 4 | "EarnerIndex": 0, 5 | "EarnerTreeProof": "0x", 6 | "EarnerLeaf": { 7 | "Earner": "0x0d6ba28b9919cfcdb6b233469cc5ce30b979e08e", 8 | "EarnerTokenRoot": "0x12104ce9deb6e62ef479656476be1da6d71905234252cf9a5c9733e6cb115d77" 9 | }, 10 | "LeafIndices": [ 11 | 0, 12 | 2, 13 | 3 14 | ], 15 | "TokenTreeProofs": [ 16 | "0xf027f6a1f6522a5ede64f16448001378272bcc3b4d9b2660f241ea87d4cafc880d5fa8201813790dcfddb37966811813117ab78db2cce941c8b1d1f3888cccca6271c73f26f5e492cc990b25f85410beaf6f04958410162dc16eb5e3ce4791ce", 17 | "0x9ae7a20b0f244cb25a1c18339432139587ecc0058130e9ebb10a98049981395d5bef23d3c6b22f7b1265e30a68e83b675394ea57b7fa150f5ecc07bae7e3b88d6271c73f26f5e492cc990b25f85410beaf6f04958410162dc16eb5e3ce4791ce", 18 | "0x31335a07ff9047e08d712967b2ef1720f8682cb696d9df15902602b0c5eebdde5bef23d3c6b22f7b1265e30a68e83b675394ea57b7fa150f5ecc07bae7e3b88d6271c73f26f5e492cc990b25f85410beaf6f04958410162dc16eb5e3ce4791ce" 19 | ], 20 | "TokenLeaves": [ 21 | { 22 | "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", 23 | "CumulativeEarnings": 1000000000000000000 24 | }, 25 | { 26 | "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", 27 | "CumulativeEarnings": 3000000000000000000 28 | }, 29 | { 30 | "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", 31 | "CumulativeEarnings": 1000000000000000000 32 | } 33 | ], 34 | "TokenTreeProofsNum": 3, 35 | "TokenLeavesNum": 3 36 | } -------------------------------------------------------------------------------- /src/test/test-data/rewardsCoordinator/processClaimProofs_SingleTokenLeaf.json: -------------------------------------------------------------------------------- 1 | { 2 | "Root": "0x86867b737e68a56280554c0447ac6b43ba1cb68f4a46d1d9a0ecb919f912959b", 3 | "RootIndex": 0, 4 | "EarnerIndex": 3, 5 | "EarnerTreeProof": "0x04da796e892869089dfdaae7269c8eb12548f6aa3774a747322b65c42d1ca0050d38d6d5e7e9e65ba24c275fcc0f0c377d8fb6ed089770d849c39a1829d1edf27e78a529aa8f867a3777f97541a23fb1844d6ae24c3b8ca1cc981510e5d08bda", 6 | "EarnerLeaf": { 7 | "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", 8 | "EarnerTokenRoot": "0x167f18d55815451f979244946b7eb2ce019c323a9e02fba1b2e05e19a27b91b5" 9 | }, 10 | "LeafIndices": [ 11 | 0 12 | ], 13 | "TokenTreeProofs": [ 14 | "0x" 15 | ], 16 | "TokenLeaves": [ 17 | { 18 | "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", 19 | "CumulativeEarnings": 1000000000000000000 20 | } 21 | ], 22 | "TokenTreeProofsNum": 1, 23 | "TokenLeavesNum": 1 24 | } -------------------------------------------------------------------------------- /src/test/test-data/rewardsCoordinator/processClaim_Preprod_Test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Root": "0x5e3227269ddcd5ddc7bc76ef42243a16166e86f9adab6183ee49b9875f2c7002", 3 | "RootIndex": 0, 4 | "EarnerIndex": 1, 5 | "EarnerTreeProof": "0x468cfb9f23175803c35daccc788454e82e192aa69bd770b26861dbe1fb42336d44b3fc6ad9da7ba33382006677e1808659ccd50c35705233f0fedca7b34c0e07d8894c83317a23b1f54108d86045843d34a127d92fb262452a636d0d40b577e1", 6 | "EarnerLeaf": { 7 | "Earner": "0x2222aac0c980cc029624b7ff55b88bc6f63c538f", 8 | "EarnerTokenRoot": "0x987aa019b1caf3fb0eaf02ebc1b8ce46840aee48d94ee21a2753045805ae38a8" 9 | }, 10 | "LeafIndices": [ 11 | 0 12 | ], 13 | "TokenTreeProofs": [ 14 | "0x" 15 | ], 16 | "TokenLeaves": [ 17 | { 18 | "Token": "0x94373a4919b3240d86ea41593d5eba789fef3848", 19 | "CumulativeEarnings": 1003827094673442500 20 | } 21 | ], 22 | "TokenTreeProofsNum": 1, 23 | "TokenLeavesNum": 1 24 | } -------------------------------------------------------------------------------- /src/test/tree/PermissionControllerUnit.tree: -------------------------------------------------------------------------------- 1 | . 2 | └── PermissionController (**** denotes that integration tests are needed to fully validate path) 3 | ├── when setAdmin is called 4 | │ ├── given that the current admin it not set 5 | │ │ └── given that the caller is not the account 6 | │ │ └── it should revert 7 | │ ├── given that the current admin is set 8 | │ │ └── given that the msg.sender is not the current admin 9 | │ │ └── it should revert 10 | │ ├── given that the new admin is the zero address 11 | │ │ └── it should revert 12 | │ └── given that a valid caller sets a valid admin 13 | │ └── it should update the permissions of the account & emit an AdminSet event 14 | ├── when setAppointee is called 15 | │ ├── given that the caller is not the admin 16 | │ │ └── it should revert 17 | │ ├── given that the appointee already has permissions 18 | │ │ └── it should revert 19 | │ └── given that proper permissions are set 20 | │ └── it should emit a DelegateSet event, and update the `appointeePermissions` and `permissionAppointee` mappings for the account 21 | └── when removeAppointee is called 22 | ├── given that the caller is not the admin 23 | │ └── it should revert 24 | ├── given that the appointee does not have permissions 25 | │ └── it should revert 26 | └── given that proper permissions are set 27 | └── it should emit a DelegateRemoved event, and update the `appointeePermissions` and `permissionAppointee` mappings for the account -------------------------------------------------------------------------------- /src/test/unit/DeployFromScratch.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {Test, console2} from "forge-std/Test.sol"; 5 | import {DeployFromScratch} from "script/deploy/local/Deploy_From_Scratch.s.sol"; 6 | 7 | // NOTE: Run the following command to deploy from scratch in an anvil instance: 8 | // RUST_LOG=forge,foundry=trace forge script script/deploy/local/Deploy_From_Scratch.s.sol --slow \ 9 | // --rpc-url http://127.0.0.1:8545 \ 10 | // --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ 11 | // --broadcast \ 12 | // --sig "run(string memory configFile)" \ 13 | // -- local/deploy_from_scratch.anvil.config.json 14 | contract DeployTest is Test { 15 | DeployFromScratch public deployer; 16 | 17 | function setUp() public { 18 | deployer = new DeployFromScratch(); 19 | } 20 | 21 | function test_DeployFromScratch() public { 22 | // Deploy, expecting no revert. 23 | deployer.run("local/deploy_from_scratch.anvil.config.json"); 24 | } 25 | } -------------------------------------------------------------------------------- /src/test/unit/PauserRegistryUnit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import "../../contracts/interfaces/IPausable.sol"; 7 | import "../../contracts/permissions/PauserRegistry.sol"; 8 | 9 | contract PauserRegistryUnitTests is Test { 10 | 11 | Vm cheats = Vm(VM_ADDRESS); 12 | 13 | PauserRegistry public pauserRegistry; 14 | 15 | address public pauser = address(555); 16 | address public unpauser = address(999); 17 | 18 | mapping(address => bool) public isExcludedFuzzAddress; 19 | 20 | event PauserStatusChanged(address pauser, bool canPause); 21 | 22 | event UnpauserChanged(address previousUnpauser, address newUnpauser); 23 | 24 | function setUp() virtual public { 25 | address[] memory pausers = new address[](1); 26 | pausers[0] = pauser; 27 | pauserRegistry = new PauserRegistry(pausers, unpauser); 28 | } 29 | 30 | function testSetIsPauserTrue(address newPauser) public { 31 | cheats.assume(newPauser != address(0)); 32 | 33 | cheats.startPrank(pauserRegistry.unpauser()); 34 | cheats.expectEmit(true, true, true, true, address(pauserRegistry)); 35 | emit PauserStatusChanged(newPauser, true); 36 | pauserRegistry.setIsPauser(newPauser, true); 37 | cheats.stopPrank(); 38 | 39 | require(pauserRegistry.isPauser(newPauser), "newPauser not set correctly"); 40 | } 41 | 42 | function testSetIsPauserFalse() public { 43 | cheats.startPrank(pauserRegistry.unpauser()); 44 | cheats.expectEmit(true, true, true, true, address(pauserRegistry)); 45 | emit PauserStatusChanged(pauser, false); 46 | pauserRegistry.setIsPauser(pauser, false); 47 | cheats.stopPrank(); 48 | 49 | require(!pauserRegistry.isPauser(pauser), "pauser not set correctly"); 50 | } 51 | 52 | function testSetUnpauser(address newUnpauser) public { 53 | cheats.assume(newUnpauser != address(0)); 54 | 55 | cheats.startPrank(pauserRegistry.unpauser()); 56 | address oldAddress = pauserRegistry.unpauser(); 57 | cheats.expectEmit(true, true, true, true, address(pauserRegistry)); 58 | emit UnpauserChanged(oldAddress, newUnpauser); 59 | pauserRegistry.setUnpauser(newUnpauser); 60 | cheats.stopPrank(); 61 | 62 | require(pauserRegistry.unpauser() == newUnpauser, "pauser not set correctly"); 63 | } 64 | 65 | function testSetPauser_RevertsWhenCallingFromNotUnpauser(address notUnpauser, address newPauser) public { 66 | cheats.assume(notUnpauser != pauserRegistry.unpauser()); 67 | cheats.assume(newPauser != address(0)); 68 | 69 | cheats.startPrank(notUnpauser); 70 | cheats.expectRevert(IPausable.OnlyUnpauser.selector); 71 | pauserRegistry.setIsPauser(newPauser, true); 72 | cheats.stopPrank(); 73 | } 74 | 75 | function testSetUnpauser_RevertsWhenCallingFromNotUnpauser(address notUnpauser, address newUnpauser) public { 76 | cheats.assume(notUnpauser != pauserRegistry.unpauser()); 77 | cheats.assume(newUnpauser != address(0)); 78 | 79 | cheats.startPrank(notUnpauser); 80 | cheats.expectRevert(IPausable.OnlyUnpauser.selector); 81 | pauserRegistry.setUnpauser(newUnpauser); 82 | cheats.stopPrank(); 83 | } 84 | 85 | function testSetPauser_RevertsWhenSettingToZeroAddress() public { 86 | address newPauser = address(0); 87 | 88 | cheats.startPrank(pauserRegistry.unpauser()); 89 | cheats.expectRevert(IPauserRegistry.InputAddressZero.selector); 90 | pauserRegistry.setIsPauser(newPauser, true); 91 | cheats.stopPrank(); 92 | } 93 | 94 | function testSetUnpauser_RevertsWhenSettingToZeroAddress() public { 95 | address newUnpauser = address(0); 96 | 97 | cheats.startPrank(pauserRegistry.unpauser()); 98 | cheats.expectRevert(IPauserRegistry.InputAddressZero.selector); 99 | pauserRegistry.setUnpauser(newUnpauser); 100 | cheats.stopPrank(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/unit/libraries/SnapshotsUnit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "forge-std/Test.sol"; 5 | import "src/contracts/libraries/Snapshots.sol"; 6 | 7 | contract SnapshotsHarness { 8 | using Snapshots for Snapshots.DefaultWadHistory; 9 | using Snapshots for Snapshots.DefaultZeroHistory; 10 | 11 | Snapshots.DefaultWadHistory internal wadHistory; 12 | Snapshots.DefaultZeroHistory internal zeroHistory; 13 | 14 | function pushWad(uint32 key, uint64 value) public { 15 | wadHistory.push(key, value); 16 | } 17 | 18 | function upperLookupWad(uint32 key) public view returns (uint64) { 19 | return wadHistory.upperLookup(key); 20 | } 21 | 22 | function latestWad() public view returns (uint64) { 23 | return wadHistory.latest(); 24 | } 25 | 26 | function lengthWad() public view returns (uint256) { 27 | return wadHistory.length(); 28 | } 29 | 30 | function pushZero(uint32 key, uint256 value) public { 31 | zeroHistory.push(key, value); 32 | } 33 | 34 | function upperLookupZero(uint32 key) public view returns (uint256) { 35 | return zeroHistory.upperLookup(key); 36 | } 37 | 38 | function latestZero() public view returns (uint256) { 39 | return zeroHistory.latest(); 40 | } 41 | 42 | function lengthZero() public view returns (uint256) { 43 | return zeroHistory.length(); 44 | } 45 | } 46 | 47 | contract SnapshotsUnitTests is Test { 48 | SnapshotsHarness harness; 49 | 50 | function setUp() public { 51 | harness = new SnapshotsHarness(); 52 | } 53 | 54 | function test_Revert_InvalidSnapshotOrdering(uint256 r) public { 55 | uint32 key = uint32(bound(r, 1, type(uint32).max)); 56 | uint32 smallerKey = uint32(bound(r, 0, key - 1)); 57 | 58 | harness.pushWad(key, 1); 59 | 60 | vm.expectRevert(Snapshots.InvalidSnapshotOrdering.selector); 61 | harness.pushWad(smallerKey, 2); 62 | } 63 | 64 | function test_Push_Correctness(uint256 r) public { 65 | uint32 key = uint32(bound(r, 0, type(uint32).max)); 66 | uint64 value = uint32(bound(r, 0, type(uint64).max)); 67 | 68 | harness.pushWad(key, value); 69 | 70 | assertEq(harness.upperLookupWad(key), value); 71 | assertEq(harness.latestWad(), value); 72 | assertEq(harness.lengthWad(), 1); 73 | } 74 | 75 | function test_UpperLookup_InitiallyWad(uint32 r) public view { 76 | assertEq(harness.upperLookupWad(r), 1e18); 77 | } 78 | 79 | function test_Latest_InitiallyWad() public view { 80 | assertEq(harness.latestWad(), 1e18); 81 | } 82 | 83 | function test_Length_InitiallyZero() public view { 84 | assertEq(harness.lengthWad(), 0); 85 | } 86 | 87 | function test_Revert_InvalidSnapshotOrdering_ZeroHistory(uint256 r) public { 88 | uint32 key = uint32(bound(r, 1, type(uint32).max)); 89 | uint32 smallerKey = uint32(bound(r, 0, key - 1)); 90 | 91 | harness.pushZero(key, 1); 92 | 93 | vm.expectRevert(Snapshots.InvalidSnapshotOrdering.selector); 94 | harness.pushZero(smallerKey, 2); 95 | } 96 | 97 | function test_Push_Correctness_ZeroHistory(uint256 r) public { 98 | uint32 key = uint32(bound(r, 0, type(uint32).max)); 99 | uint256 value = bound(r, 0, type(uint224).max); 100 | 101 | harness.pushZero(key, value); 102 | 103 | assertEq(harness.upperLookupZero(key), value); 104 | assertEq(harness.latestZero(), value); 105 | assertEq(harness.lengthZero(), 1); 106 | } 107 | 108 | function test_UpperLookup_InitiallyZero(uint32 r) public view { 109 | assertEq(harness.upperLookupZero(r), 0); 110 | } 111 | 112 | function test_Latest_InitiallyZero() public view { 113 | assertEq(harness.latestZero(), 0); 114 | } 115 | 116 | function test_Length_InitiallyZero_ZeroHistory() public view { 117 | assertEq(harness.lengthZero(), 0); 118 | } 119 | } -------------------------------------------------------------------------------- /src/test/unit/mixins/SignatureUtilsUnit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity ^0.8.27; 3 | 4 | import "forge-std/Test.sol"; 5 | import "src/contracts/mixins/SignatureUtils.sol"; 6 | 7 | contract MockSigner { 8 | mapping(bytes32 => mapping(bytes => bool)) public validSignatures; 9 | 10 | function setValidSignature(bytes32 digest, bytes memory signature, bool valid) public { 11 | validSignatures[digest][signature] = valid; 12 | } 13 | 14 | function isValidSignatureNow(bytes32 digest, bytes memory signature) public view returns (bool) { 15 | return validSignatures[digest][signature]; 16 | } 17 | } 18 | 19 | contract SignatureUtilsHarness is SignatureUtils { 20 | function calculateSignableDigest(bytes32 hash) public view returns (bytes32) { 21 | return _calculateSignableDigest(hash); 22 | } 23 | 24 | function checkIsValidSignatureNow( 25 | address signer, 26 | bytes32 digest, 27 | bytes memory signature, 28 | uint256 expiry 29 | ) public view { 30 | _checkIsValidSignatureNow(signer, digest, signature, expiry); 31 | } 32 | } 33 | 34 | contract SignatureUtilsUnit is Test { 35 | SignatureUtilsHarness harness; 36 | MockSigner mockSigner; 37 | uint256 signerPk; 38 | address signer; 39 | bytes32 hash; 40 | bytes32 digest; 41 | bytes32 expectedDomainSeparator; 42 | 43 | function setUp() public { 44 | vm.chainId(1); 45 | 46 | harness = new SignatureUtilsHarness(); 47 | mockSigner = new MockSigner(); 48 | signerPk = 1; 49 | signer = vm.addr(signerPk); 50 | 51 | hash = keccak256(""); 52 | digest = harness.calculateSignableDigest(hash); 53 | 54 | expectedDomainSeparator = keccak256( 55 | abi.encode( 56 | keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"), 57 | keccak256(bytes("EigenLayer")), 58 | block.chainid, 59 | address(harness) 60 | ) 61 | ); 62 | } 63 | 64 | function test_domainSeparator_NonZero() public view { 65 | assertTrue(harness.domainSeparator() != 0, "The domain separator should be non-zero"); 66 | assertTrue(harness.domainSeparator() == expectedDomainSeparator, "The domain separator should be as expected"); 67 | } 68 | 69 | function test_domainSeparator_NewChainId() public { 70 | bytes32 initialDomainSeparator = harness.domainSeparator(); 71 | 72 | // Change the chain ID 73 | vm.chainId(9999); 74 | 75 | bytes32 newDomainSeparator = harness.domainSeparator(); 76 | 77 | assertTrue(newDomainSeparator != 0, "The new domain separator should be non-zero"); 78 | assertTrue( 79 | initialDomainSeparator != newDomainSeparator, 80 | "The domain separator should change when the chain ID changes" 81 | ); 82 | } 83 | 84 | function test_checkIsValidSignatureNow_Expired() public { 85 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, digest); 86 | 87 | vm.expectRevert(ISignatureUtils.SignatureExpired.selector); 88 | harness.checkIsValidSignatureNow(signer, digest, abi.encode(r, s, v), block.timestamp - 1); 89 | } 90 | 91 | function test_Revert_checkIsValidSignatureNow_InvalidSignature() public { 92 | vm.expectRevert(ISignatureUtils.InvalidSignature.selector); 93 | harness.checkIsValidSignatureNow(signer, digest, "", block.timestamp); 94 | } 95 | } --------------------------------------------------------------------------------