From 2990aac0c1d7e2cc64bfee45ed2599faa4130993 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 12 Dec 2024 10:34:34 +0100 Subject: [PATCH 01/37] remove unused function from interface --- mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol | 1 - mainnet-contracts/src/interface/EigenLayer/IStrategy.sol | 5 ----- 2 files changed, 6 deletions(-) diff --git a/mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol b/mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol index c67d4030..ff033e8c 100644 --- a/mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol +++ b/mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol @@ -18,7 +18,6 @@ interface IEigenLayer { * the data is resubmitted and the hash of the submitted data is computed by `calculateWithdrawalRoot` and checked against the * stored hash in order to confirm the integrity of the submitted data. */ - struct QueuedWithdrawal { IStrategy[] strategies; uint256[] shares; diff --git a/mainnet-contracts/src/interface/EigenLayer/IStrategy.sol b/mainnet-contracts/src/interface/EigenLayer/IStrategy.sol index 853bd2c4..773a699c 100644 --- a/mainnet-contracts/src/interface/EigenLayer/IStrategy.sol +++ b/mainnet-contracts/src/interface/EigenLayer/IStrategy.sol @@ -2,11 +2,6 @@ pragma solidity >=0.8.0 <0.9.0; interface IStrategy { - /** - * @notice Returns the amount of underlying tokens for `user` - */ - function userUnderlying(address user) external view returns (uint256); - /** * @notice Returns the amount of underlying tokens for `user` */ From c7f7833c94ab37136654b5ccf1606d451b22a7f5 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 12 Dec 2024 13:39:12 +0100 Subject: [PATCH 02/37] refactor / remove everything --- mainnet-contracts/package.json | 1 - .../script/CompleteQueuedWithdrawals.s.sol | 4 +- mainnet-contracts/script/DeployPufETH.s.sol | 4 +- mainnet-contracts/script/DeployPuffer.s.sol | 8 +- .../DeployPufferModuleImplementation.s.sol | 4 +- .../script/DeployPufferVaultV3.s.sol | 8 +- .../script/DeployRestakingOperator.s.sol | 8 +- mainnet-contracts/script/UpgradePufETH.s.sol | 6 +- .../script/UpgradePufferModule.s.sol | 4 +- mainnet-contracts/src/PufferModule.sol | 25 +- mainnet-contracts/src/PufferModuleManager.sol | 137 +- mainnet-contracts/src/PufferVault.sol | 4 +- mainnet-contracts/src/PufferVaultV2.sol | 74 +- mainnet-contracts/src/PufferVaultV3.sol | 6 +- mainnet-contracts/src/PufferVaultV4.sol | 6 +- mainnet-contracts/src/RestakingOperator.sol | 107 +- .../EigenLayer/IDelegationManager.sol | 188 --- .../EigenLayer/IRewardsCoordinator.sol | 20 - .../src/interface/EigenLayer/IStrategy.sol | 18 - .../Eigenlayer-Slashing/IAVSDirectory.sol | 142 ++ .../Eigenlayer-Slashing/IAVSRegistrar.sol | 22 + .../IAllocationManager.sol | 525 +++++++ .../Eigenlayer-Slashing/IBackingEigen.sol | 65 + .../IDelegationManager.sol | 491 +++++++ .../Eigenlayer-Slashing/IETHPOSDeposit.sol | 41 + .../interface/Eigenlayer-Slashing/IEigen.sol | 53 + .../IEigenLayer.sol | 1 + .../Eigenlayer-Slashing/IEigenPod.sol | 338 +++++ .../Eigenlayer-Slashing/IEigenPodManager.sol | 151 ++ .../Eigenlayer-Slashing/IPausable.sol | 66 + .../Eigenlayer-Slashing/IPauserRegistry.sol | 22 + .../IPermissionController.sol | 147 ++ .../IRewardsCoordinator.sol | 597 ++++++++ .../Eigenlayer-Slashing/IShareManager.sol | 36 + .../Eigenlayer-Slashing/ISignatureUtils.sol | 30 + .../Eigenlayer-Slashing/IStrategy.sol | 31 + .../Eigenlayer-Slashing/IStrategyFactory.sol | 60 + .../Eigenlayer-Slashing/IStrategyManager.sol | 174 +++ .../src/interface/IPufferModule.sol | 10 +- .../src/interface/IPufferModuleManager.sol | 172 +-- .../IRegistryCoordinatorExtended.sol | 2 +- .../src/interface/IRestakingOperator.sol | 122 -- .../interface/libraries/BeaconChainProofs.sol | 303 ++++ .../src/interface/libraries/BytesLib.sol | 479 +++++++ .../src/interface/libraries/Endian.sol | 25 + .../src/interface/libraries/Merkle.sol | 163 +++ .../interface/libraries/OperatorSetLib.sol | 21 + .../src/interface/libraries/SlashingLib.sol | 170 +++ .../src/interface/libraries/Snapshots.sol | 182 +++ .../src/struct/ModuleStorage.sol | 2 +- .../PufferRevenueDepositor.fork.t.sol | 6 +- .../Integration/PufferTest.integration.t.sol | 8 +- .../PufferVaultV2WithdrawFromEl.fork.t.sol | 7 +- .../test/MainnetForkTestHelper.sol | 6 +- .../PufferModuleManager.integration.t.sol | 31 +- ...anagerHoleskyTestnetTest.integration.t.sol | 13 +- ...ifyWithdrawalCredentials.integration.t.sol | 30 - .../ffi/PufferModuleManagerHoleskyFfi.t.sol | 14 +- .../test/mocks/DelegationManagerMock.sol | 8 +- .../mocks/EigenLayerDelegationManagerMock.sol | 311 ++++- .../test/mocks/EigenLayerManagerMock.sol | 4 +- .../test/mocks/EigenPodManagerMock.sol | 187 +-- mainnet-contracts/test/mocks/PausableMock.sol | 9 +- .../test/mocks/PufferVaultV2Tests.sol | 6 +- .../test/mocks/PufferVaultV4Tests.sol | 6 +- mainnet-contracts/test/mocks/SlasherMock.sol | 1226 +++++++++-------- .../test/mocks/stETHStrategyMock.sol | 2 +- .../test/mocks/stETHStrategyTestnet.sol | 2 +- .../test/unit/PufferModuleManager.t.sol | 18 +- yarn.lock | 6 - 70 files changed, 5518 insertions(+), 1657 deletions(-) delete mode 100644 mainnet-contracts/src/interface/EigenLayer/IDelegationManager.sol delete mode 100644 mainnet-contracts/src/interface/EigenLayer/IRewardsCoordinator.sol delete mode 100644 mainnet-contracts/src/interface/EigenLayer/IStrategy.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSRegistrar.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IAllocationManager.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IBackingEigen.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IETHPOSDeposit.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigen.sol rename mainnet-contracts/src/interface/{EigenLayer => Eigenlayer-Slashing}/IEigenLayer.sol (99%) create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPodManager.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IPausable.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IPauserRegistry.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IShareManager.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/ISignatureUtils.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategy.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyFactory.sol create mode 100644 mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyManager.sol delete mode 100644 mainnet-contracts/src/interface/IRestakingOperator.sol create mode 100644 mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol create mode 100644 mainnet-contracts/src/interface/libraries/BytesLib.sol create mode 100644 mainnet-contracts/src/interface/libraries/Endian.sol create mode 100644 mainnet-contracts/src/interface/libraries/Merkle.sol create mode 100644 mainnet-contracts/src/interface/libraries/OperatorSetLib.sol create mode 100644 mainnet-contracts/src/interface/libraries/SlashingLib.sol create mode 100644 mainnet-contracts/src/interface/libraries/Snapshots.sol delete mode 100644 mainnet-contracts/test/fork-tests/VerifyWithdrawalCredentials.integration.t.sol diff --git a/mainnet-contracts/package.json b/mainnet-contracts/package.json index 9463ff26..2c1c28fb 100644 --- a/mainnet-contracts/package.json +++ b/mainnet-contracts/package.json @@ -10,7 +10,6 @@ "@connext/interfaces": "^2.0.5", "@openzeppelin/contracts": "5.0.1", "@openzeppelin/contracts-upgradeable": "5.0.1", - "eigenlayer-contracts": "https://github.com/Layr-Labs/eigenlayer-contracts.git#4478eb6", "eigenlayer-middleware": "https://github.com/bxmmm1/eigenlayer-middleware.git#dbf6c1a", "l2-contracts": "*", "murky": "https://github.com/dmfxyz/murky.git", diff --git a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol index 20ef65c6..c8afbc06 100644 --- a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol +++ b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol @@ -4,8 +4,8 @@ pragma solidity >=0.8.0 <0.9.0; import { Script } from "forge-std/Script.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; -import { IStrategy } from "eigenlayer/interfaces/IStrategy.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; struct ScriptParameters { address pufferModuleManager; diff --git a/mainnet-contracts/script/DeployPufETH.s.sol b/mainnet-contracts/script/DeployPufETH.s.sol index c1ff528b..996231aa 100644 --- a/mainnet-contracts/script/DeployPufETH.s.sol +++ b/mainnet-contracts/script/DeployPufETH.s.sol @@ -10,8 +10,8 @@ import { PufferVault } from "../src/PufferVault.sol"; import { Timelock } from "../src/Timelock.sol"; import { NoImplementation } from "../src/NoImplementation.sol"; import { PufferDeployment } from "../src/structs/PufferDeployment.sol"; -import { IEigenLayer } from "../src/interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "../src/interface/EigenLayer/IStrategy.sol"; +import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { stETHMock } from "../test/mocks/stETHMock.sol"; diff --git a/mainnet-contracts/script/DeployPuffer.s.sol b/mainnet-contracts/script/DeployPuffer.s.sol index e4123abd..851550af 100644 --- a/mainnet-contracts/script/DeployPuffer.s.sol +++ b/mainnet-contracts/script/DeployPuffer.s.sol @@ -13,8 +13,8 @@ import { stdJson } from "forge-std/StdJson.sol"; import { EigenPodManagerMock } from "../test/mocks/EigenPodManagerMock.sol"; import { DelegationManagerMock } from "../test/mocks/DelegationManagerMock.sol"; import { BeaconMock } from "../test/mocks/BeaconMock.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISlasher } from "eigenlayer/interfaces/ISlasher.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; @@ -24,7 +24,7 @@ import { ValidatorTicketPricer } from "../src/ValidatorTicketPricer.sol"; import { OperationsCoordinator } from "../src/OperationsCoordinator.sol"; import { PufferOracleV2 } from "../src/PufferOracleV2.sol"; import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { AVSContractsRegistry } from "../src/AVSContractsRegistry.sol"; import { RewardsCoordinatorMock } from "../test/mocks/RewardsCoordinatorMock.sol"; @@ -135,7 +135,7 @@ contract DeployPuffer is BaseScript { RestakingOperator restakingOperatorImplementation = new RestakingOperator( IDelegationManager(delegationManager), - ISlasher(eigenSlasher), + IAllocationManager(eigenSlasher), PufferModuleManager(payable(address(moduleManagerProxy))), IRewardsCoordinator(rewardsCoordinator) ); diff --git a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol index 120918c0..98c1c564 100644 --- a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol +++ b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol @@ -12,9 +12,9 @@ import { stdJson } from "forge-std/StdJson.sol"; import { GuardianModule } from "../src/GuardianModule.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { PufferModule } from "../src/PufferModule.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; /** diff --git a/mainnet-contracts/script/DeployPufferVaultV3.s.sol b/mainnet-contracts/script/DeployPufferVaultV3.s.sol index 2e14f1c3..9df636e1 100644 --- a/mainnet-contracts/script/DeployPufferVaultV3.s.sol +++ b/mainnet-contracts/script/DeployPufferVaultV3.s.sol @@ -9,12 +9,10 @@ import { PufferVaultV3 } from "src/PufferVaultV3.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; -import { IStrategy } from "src/interface/EigenLayer/IStrategy.sol"; -import { IEigenLayer } from "src/interface/EigenLayer/IEigenLayer.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/EigenLayer/IDelegationManager.sol"; -import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; -import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; /** * @title DeployPufferVaultV3 diff --git a/mainnet-contracts/script/DeployRestakingOperator.s.sol b/mainnet-contracts/script/DeployRestakingOperator.s.sol index d37d359a..cee7411e 100644 --- a/mainnet-contracts/script/DeployRestakingOperator.s.sol +++ b/mainnet-contracts/script/DeployRestakingOperator.s.sol @@ -6,10 +6,10 @@ import { IPufferModuleManager } from "../src/interface/IPufferModuleManager.sol" import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { RestakingOperator } from "../src/RestakingOperator.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISlasher } from "eigenlayer/interfaces/ISlasher.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; /** @@ -24,7 +24,7 @@ contract DeployRestakingOperator is DeployerHelper { RestakingOperator restakingOperatorImplementation = new RestakingOperator({ delegationManager: IDelegationManager(_getEigenDelegationManager()), - slasher: ISlasher(_getEigenSlasher()), + slasher: IAllocationManager(_getEigenSlasher()), moduleManager: IPufferModuleManager(_getPufferModuleManager()), rewardsCoordinator: IRewardsCoordinator(_getRewardsCoordinator()) }); diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index f07ddbcd..a77742d6 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -7,9 +7,9 @@ import { PufferVault } from "../src/PufferVault.sol"; import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; -import { IEigenLayer } from "../src/interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "../src/interface/EigenLayer/IStrategy.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer/IDelegationManager.sol"; +import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { LidoWithdrawalQueueMock } from "../test/mocks/LidoWithdrawalQueueMock.sol"; diff --git a/mainnet-contracts/script/UpgradePufferModule.s.sol b/mainnet-contracts/script/UpgradePufferModule.s.sol index 3ed3ab62..ba995e17 100644 --- a/mainnet-contracts/script/UpgradePufferModule.s.sol +++ b/mainnet-contracts/script/UpgradePufferModule.s.sol @@ -10,8 +10,8 @@ import { BaseScript } from "script/BaseScript.s.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { PufferModule } from "../src/PufferModule.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer/IRewardsCoordinator.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; /** * @title UpgradePufferModule diff --git a/mainnet-contracts/src/PufferModule.sol b/mainnet-contracts/src/PufferModule.sol index 5c3d60fa..4e968777 100644 --- a/mainnet-contracts/src/PufferModule.sol +++ b/mainnet-contracts/src/PufferModule.sol @@ -3,12 +3,12 @@ pragma solidity >=0.8.0 <0.9.0; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { IEigenPodManager } from "eigenlayer/interfaces/IEigenPodManager.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; -import { IStrategy } from "eigenlayer/interfaces/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IEigenPodManager } from "../src/interface/EigenLayer-Slashing/IEigenPodManager.sol"; +import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; -import { IEigenPod } from "eigenlayer/interfaces/IEigenPod.sol"; +import { IEigenPod } from "../src/interface/EigenLayer-Slashing/IEigenPod.sol"; import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; import { IPufferModule } from "./interface/IPufferModule.sol"; import { Unauthorized } from "./Errors.sol"; @@ -16,7 +16,8 @@ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/I import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ModuleStorage } from "./struct/ModuleStorage.sol"; -import { IRewardsCoordinator } from "./interface/EigenLayer/IRewardsCoordinator.sol"; +import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; /** * @title PufferModule @@ -156,8 +157,8 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable onlyPufferModuleManager returns (bytes32[] memory) { - IDelegationManager.QueuedWithdrawalParams[] memory withdrawals = - new IDelegationManager.QueuedWithdrawalParams[](1); + IDelegationManagerTypes.QueuedWithdrawalParams[] memory withdrawals = + new IDelegationManagerTypes.QueuedWithdrawalParams[](1); uint256[] memory shares = new uint256[](1); shares[0] = shareAmount; @@ -165,9 +166,9 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable IStrategy[] memory strategies = new IStrategy[](1); strategies[0] = IStrategy(_BEACON_CHAIN_STRATEGY); - withdrawals[0] = IDelegationManager.QueuedWithdrawalParams({ + withdrawals[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ strategies: strategies, - shares: shares, + depositShares: shares, withdrawer: address(this) }); @@ -178,15 +179,13 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable * @inheritdoc IPufferModule */ function completeQueuedWithdrawals( - IDelegationManager.Withdrawal[] calldata withdrawals, + IDelegationManagerTypes.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens ) external virtual whenNotPaused onlyPufferModuleManager { EIGEN_DELEGATION_MANAGER.completeQueuedWithdrawals({ withdrawals: withdrawals, tokens: tokens, - middlewareTimesIndexes: middlewareTimesIndexes, receiveAsTokens: receiveAsTokens }); } diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 53c14827..2c48bf81 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.0 <0.9.0; import { IPufferModule } from "./interface/IPufferModule.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { Unauthorized, InvalidAmount } from "./Errors.sol"; -import { IRestakingOperator } from "./interface/IRestakingOperator.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { PufferModule } from "./PufferModule.sol"; import { PufferVaultV3 } from "./PufferVaultV3.sol"; @@ -15,11 +14,13 @@ import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol"; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; import { AVSContractsRegistry } from "./AVSContractsRegistry.sol"; +import { RestakingOperator } from "./RestakingOperator.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; /** * @title PufferModuleManager @@ -88,7 +89,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, */ function callCompleteQueuedWithdrawals( bytes32 moduleName, - IDelegationManager.Withdrawal[] calldata withdrawals, + IDelegationManagerTypes.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens @@ -106,8 +107,8 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, for (uint256 i = 0; i < withdrawals.length; ++i) { // nosemgrep array-length-outside-loop - for (uint256 j = 0; j < withdrawals[i].shares.length; ++j) { - sharesWithdrawn += withdrawals[i].shares[j]; + for (uint256 j = 0; j < withdrawals[i].scaledShares.length; ++j) { + sharesWithdrawn += withdrawals[i].scaledShares[j]; } } @@ -115,7 +116,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the PufferProtocol * @param moduleName The name of the module */ @@ -174,7 +174,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, * @dev Restricted to the DAO */ function callSetClaimerFor(address moduleOrReOp, address claimer) external virtual restricted { - // We can cast `moduleOrReOp` to IPufferModule/IRestakingOperator, uses the same function signature. + // We can cast `moduleOrReOp` to IPufferModule/RestakingOperator, uses the same function signature. IPufferModule(moduleOrReOp).callSetClaimerFor(claimer); emit ClaimerSet({ rewardsReceiver: moduleOrReOp, claimer: claimer }); } @@ -193,17 +193,12 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ - function createNewRestakingOperator( - string calldata metadataURI, - address delegationApprover, - uint32 stakerOptOutWindowBlocks - ) external virtual restricted returns (IRestakingOperator) { - IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ - __deprecated_earningsReceiver: address(this), - delegationApprover: delegationApprover, - stakerOptOutWindowBlocks: stakerOptOutWindowBlocks - }); - + function createNewRestakingOperator(string calldata metadataURI, address delegationApprover, uint32 allocationDelay) + external + virtual + restricted + returns (RestakingOperator) + { address restakingOperator = Create2.deploy({ amount: 0, salt: keccak256(abi.encode(metadataURI)), @@ -211,14 +206,16 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, type(BeaconProxy).creationCode, abi.encode( RESTAKING_OPERATOR_BEACON, - abi.encodeCall(RestakingOperator.initialize, (authority(), operatorDetails, metadataURI)) + abi.encodeCall( + RestakingOperator.initialize, (authority(), delegationApprover, metadataURI, allocationDelay) + ) ) ) }); - emit RestakingOperatorCreated(restakingOperator, operatorDetails); + emit RestakingOperatorCreated(restakingOperator, delegationApprover); - return IRestakingOperator(restakingOperator); + return RestakingOperator(restakingOperator); } /** @@ -226,18 +223,19 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, * @dev Restricted to the DAO */ function callModifyOperatorDetails( - IRestakingOperator restakingOperator, - IDelegationManager.OperatorDetails calldata newOperatorDetails + RestakingOperator restakingOperator, + address newOperatorDetails, + address newDelegationApprover ) external virtual restricted { - restakingOperator.modifyOperatorDetails(newOperatorDetails); - emit RestakingOperatorModified(address(restakingOperator), newOperatorDetails); + restakingOperator.modifyOperatorDetails(newOperatorDetails, newDelegationApprover); + emit RestakingOperatorModified(address(restakingOperator), newDelegationApprover); } /** * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ - function callUpdateMetadataURI(IRestakingOperator restakingOperator, string calldata metadataURI) + function callUpdateMetadataURI(RestakingOperator restakingOperator, string calldata metadataURI) external virtual restricted @@ -250,7 +248,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ - function callOptIntoSlashing(IRestakingOperator restakingOperator, address slasher) external virtual restricted { + function callOptIntoSlashing(RestakingOperator restakingOperator, address slasher) external virtual restricted { restakingOperator.optIntoSlashing(slasher); emit RestakingOperatorOptedInSlasher(address(restakingOperator), slasher); } @@ -285,82 +283,31 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callRegisterOperatorToAVS( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature - ) external virtual restricted { - restakingOperator.registerOperatorToAVS({ - avsRegistryCoordinator: avsRegistryCoordinator, - quorumNumbers: quorumNumbers, - socket: socket, - params: params, - operatorSignature: operatorSignature - }); - - emit RestakingOperatorRegisteredToAVS(restakingOperator, avsRegistryCoordinator, quorumNumbers, socket); - } - - /** - * @inheritdoc IPufferModuleManager - * @dev Restricted to the DAO - */ - function callRegisterOperatorToAVSWithChurn( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - IRegistryCoordinator.OperatorKickParam[] calldata operatorKickParams, - ISignatureUtils.SignatureWithSaltAndExpiry calldata churnApproverSignature, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature + RestakingOperator restakingOperator, + IAllocationManager.RegisterParams calldata registrationParams ) external virtual restricted { - restakingOperator.registerOperatorToAVSWithChurn({ - avsRegistryCoordinator: avsRegistryCoordinator, - quorumNumbers: quorumNumbers, - socket: socket, - params: params, - operatorKickParams: operatorKickParams, - churnApproverSignature: churnApproverSignature, - operatorSignature: operatorSignature - }); + restakingOperator.registerOperatorToAVS(registrationParams); - emit RestakingOperatorRegisteredToAVSWithChurn({ - restakingOperator: restakingOperator, - avsRegistryCoordinator: avsRegistryCoordinator, - quorumNumbers: quorumNumbers, - socket: socket, - operatorKickParams: operatorKickParams - }); + emit RestakingOperatorRegisteredToAVS(address(restakingOperator), avsRegistryCoordinator, quorumNumbers, socket); } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ - function customExternalCall(IRestakingOperator restakingOperator, address target, bytes calldata customCalldata) + function customExternalCall(RestakingOperator restakingOperator, address target, bytes calldata customCalldata) external virtual restricted { - // Custom external calls are only allowed to whitelisted registry coordinators - if (!AVS_CONTRACTS_REGISTRY.isAllowedRegistryCoordinator(target, customCalldata)) { - revert Unauthorized(); - } - bytes memory response = restakingOperator.customCalldataCall(target, customCalldata); emit CustomCallSucceeded(address(restakingOperator), target, customCalldata, response); } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callStartCheckpoint(address[] calldata moduleAddresses) external virtual restricted { @@ -371,11 +318,10 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callDeregisterOperatorFromAVS( - IRestakingOperator restakingOperator, + RestakingOperator restakingOperator, address avsRegistryCoordinator, bytes calldata quorumNumbers ) external virtual restricted { @@ -385,25 +331,10 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager - * @dev Restricted to the DAO - */ - function callUpdateOperatorAVSSocket( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - string calldata socket - ) external virtual restricted { - restakingOperator.updateOperatorAVSSocket(avsRegistryCoordinator, socket); - - emit RestakingOperatorAVSSocketUpdated(restakingOperator, avsRegistryCoordinator, socket); - } - - /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function updateAVSRegistrationSignatureProof( - IRestakingOperator restakingOperator, + RestakingOperator restakingOperator, bytes32 digestHash, address signer ) external virtual restricted { diff --git a/mainnet-contracts/src/PufferVault.sol b/mainnet-contracts/src/PufferVault.sol index 7ac0e402..58809642 100644 --- a/mainnet-contracts/src/PufferVault.sol +++ b/mainnet-contracts/src/PufferVault.sol @@ -5,8 +5,8 @@ import { IPufferVault } from "./interface/IPufferVault.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer/IStrategy.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; import { PufferVaultStorage } from "./PufferVaultStorage.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; diff --git a/mainnet-contracts/src/PufferVaultV2.sol b/mainnet-contracts/src/PufferVaultV2.sol index 6ce0e113..ad885b0b 100644 --- a/mainnet-contracts/src/PufferVaultV2.sol +++ b/mainnet-contracts/src/PufferVaultV2.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVault } from "./PufferVault.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer/IStrategy.sol"; -import { IDelegationManager } from "./interface/EigenLayer/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV2 } from "./interface/IPufferVaultV2.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; @@ -472,74 +472,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { return $.exitFeeBasisPoints; } - /** - * @notice Initiates Withdrawal from EigenLayer - * Restricted access to Puffer Operations multisig - */ - function initiateStETHWithdrawalFromEigenLayer(uint256 sharesToWithdraw) external virtual override restricted { - VaultStorage storage $ = _getPufferVaultStorage(); - - IDelegationManager.QueuedWithdrawalParams[] memory withdrawals = - new IDelegationManager.QueuedWithdrawalParams[](1); - - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = IStrategy(_EIGEN_STETH_STRATEGY); - - uint256[] memory shares = new uint256[](1); - shares[0] = sharesToWithdraw; - - $.eigenLayerPendingWithdrawalSharesAmount += sharesToWithdraw; - - withdrawals[0] = IDelegationManager.QueuedWithdrawalParams({ - strategies: strategies, - shares: shares, - withdrawer: address(this) - }); - - bytes32 withdrawalRoot = _DELEGATION_MANAGER.queueWithdrawals(withdrawals)[0]; - - $.eigenLayerWithdrawals.add(withdrawalRoot); - } - - /** - * @notice Claims the queued withdrawal from EigenLayer - * Restricted access to Puffer Operations multisig - */ - function claimWithdrawalFromEigenLayerM2( - IEigenLayer.QueuedWithdrawal calldata queuedWithdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex, - uint256 nonce - ) external virtual restricted { - VaultStorage storage $ = _getPufferVaultStorage(); - - IDelegationManager.Withdrawal memory withdrawal = IDelegationManager.Withdrawal({ - staker: address(this), - delegatedTo: address(0), - withdrawer: address(this), - nonce: nonce, - startBlock: queuedWithdrawal.withdrawalStartBlock, - strategies: queuedWithdrawal.strategies, - shares: queuedWithdrawal.shares - }); - - bytes32 withdrawalRoot = _DELEGATION_MANAGER.calculateWithdrawalRoot(withdrawal); - bool isValidWithdrawal = $.eigenLayerWithdrawals.remove(withdrawalRoot); - if (!isValidWithdrawal) { - revert InvalidWithdrawal(); - } - - // nosemgrep - $.eigenLayerPendingWithdrawalSharesAmount -= queuedWithdrawal.shares[0]; - - _DELEGATION_MANAGER.completeQueuedWithdrawal({ - withdrawal: withdrawal, - tokens: tokens, - middlewareTimesIndex: middlewareTimesIndex, - receiveAsTokens: true - }); - } - // Not compatible anymore function claimWithdrawalFromEigenLayer( IEigenLayer.QueuedWithdrawal calldata queuedWithdrawal, diff --git a/mainnet-contracts/src/PufferVaultV3.sol b/mainnet-contracts/src/PufferVaultV3.sol index 38d68d96..db0a2fc4 100644 --- a/mainnet-contracts/src/PufferVaultV3.sol +++ b/mainnet-contracts/src/PufferVaultV3.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "./PufferVaultV2.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer/IStrategy.sol"; -import { IDelegationManager } from "./interface/EigenLayer/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; diff --git a/mainnet-contracts/src/PufferVaultV4.sol b/mainnet-contracts/src/PufferVaultV4.sol index 6e405720..474f1f0e 100644 --- a/mainnet-contracts/src/PufferVaultV4.sol +++ b/mainnet-contracts/src/PufferVaultV4.sol @@ -3,9 +3,9 @@ pragma solidity >=0.8.0 <0.9.0; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer/IStrategy.sol"; -import { IDelegationManager } from "./interface/EigenLayer/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; import { PufferVaultV3 } from "./PufferVaultV3.sol"; diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index 734881c5..a902b649 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -4,9 +4,8 @@ pragma solidity >=0.8.0 <0.9.0; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISlasher } from "eigenlayer/interfaces/ISlasher.sol"; -import { IRestakingOperator } from "./interface/IRestakingOperator.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; import { Unauthorized, InvalidAddress } from "./Errors.sol"; import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; @@ -14,8 +13,8 @@ import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; import { IRegistryCoordinatorExtended } from "./interface/IRegistryCoordinatorExtended.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; -import { IRewardsCoordinator } from "./interface/EigenLayer/IRewardsCoordinator.sol"; +import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IRewardsCoordinator } from "./interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; /** * @title RestakingOperator @@ -23,7 +22,7 @@ import { IRewardsCoordinator } from "./interface/EigenLayer/IRewardsCoordinator. * @notice PufferModule * @custom:security-contact security@puffer.fi */ -contract RestakingOperator is IRestakingOperator, IERC1271, Initializable, AccessManagedUpgradeable { +contract RestakingOperator is IERC1271, Initializable, AccessManagedUpgradeable { using Address for address; // keccak256(abi.encode(uint256(keccak256("RestakingOperator.storage")) - 1)) & ~bytes32(uint256(0xff)) // slither-disable-next-line unused-state @@ -61,7 +60,7 @@ contract RestakingOperator is IRestakingOperator, IERC1271, Initializable, Acces /** * @dev Upgradeable contract from EigenLayer */ - ISlasher public immutable override EIGEN_SLASHER; + IAllocationManager public immutable override EIGEN_ALLOCATION_MANAGER; /** * @dev Upgradeable Puffer Module Manager @@ -78,21 +77,21 @@ contract RestakingOperator is IRestakingOperator, IERC1271, Initializable, Acces // We use constructor to set the immutable variables constructor( IDelegationManager delegationManager, - ISlasher slasher, + IAllocationManager allocationManager, IPufferModuleManager moduleManager, IRewardsCoordinator rewardsCoordinator ) { if (address(delegationManager) == address(0)) { revert InvalidAddress(); } - if (address(slasher) == address(0)) { + if (address(allocationManager) == address(0)) { revert InvalidAddress(); } if (address(moduleManager) == address(0)) { revert InvalidAddress(); } EIGEN_DELEGATION_MANAGER = delegationManager; - EIGEN_SLASHER = slasher; + EIGEN_ALLOCATION_MANAGER = allocationManager; PUFFER_MODULE_MANAGER = moduleManager; EIGEN_REWARDS_COORDINATOR = rewardsCoordinator; _disableInitializers(); @@ -100,43 +99,29 @@ contract RestakingOperator is IRestakingOperator, IERC1271, Initializable, Acces function initialize( address initialAuthority, - IDelegationManager.OperatorDetails calldata operatorDetails, - string calldata metadataURI + address initDelegationApprover, + string calldata metadataURI, + uint32 allocationDelay ) external initializer { __AccessManaged_init(initialAuthority); - EIGEN_DELEGATION_MANAGER.registerAsOperator(operatorDetails, metadataURI); + EIGEN_DELEGATION_MANAGER.registerAsOperator(initDelegationApprover, allocationDelay, metadataURI); } /** - * @inheritdoc IRestakingOperator * @dev Restricted to the PufferModuleManager */ - function optIntoSlashing(address slasher) external virtual onlyPufferModuleManager { - EIGEN_SLASHER.optIntoSlashing(slasher); + function modifyOperatorDetails(address newDelegationApprover) external virtual onlyPufferModuleManager { + EIGEN_DELEGATION_MANAGER.modifyOperatorDetails(address(this), newDelegationApprover); } /** - * @inheritdoc IRestakingOperator - * @dev Restricted to the PufferModuleManager - */ - function modifyOperatorDetails(IDelegationManager.OperatorDetails calldata newOperatorDetails) - external - virtual - onlyPufferModuleManager - { - EIGEN_DELEGATION_MANAGER.modifyOperatorDetails(newOperatorDetails); - } - - /** - * @inheritdoc IRestakingOperator * @dev Restricted to the PufferModuleManager */ function updateOperatorMetadataURI(string calldata metadataURI) external virtual onlyPufferModuleManager { - EIGEN_DELEGATION_MANAGER.updateOperatorMetadataURI(metadataURI); + EIGEN_DELEGATION_MANAGER.updateOperatorMetadataURI(address(this), metadataURI); } /** - * @inheritdoc IRestakingOperator * @dev Restricted to the PufferModuleManager */ function updateSignatureProof(bytes32 digestHash, address signer) external virtual onlyPufferModuleManager { @@ -146,86 +131,40 @@ contract RestakingOperator is IRestakingOperator, IERC1271, Initializable, Acces } /** - * @inheritdoc IRestakingOperator * @dev Restricted to the PufferModuleManager */ - function registerOperatorToAVS( - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature - ) external virtual onlyPufferModuleManager { - IRegistryCoordinatorExtended(avsRegistryCoordinator).registerOperator({ - quorumNumbers: quorumNumbers, - socket: socket, - params: params, - operatorSignature: operatorSignature - }); - } - - /** - * @inheritdoc IRestakingOperator - * @dev Restricted to the PufferModuleManager - */ - function registerOperatorToAVSWithChurn( - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - IRegistryCoordinator.OperatorKickParam[] calldata operatorKickParams, - ISignatureUtils.SignatureWithSaltAndExpiry calldata churnApproverSignature, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature - ) external virtual onlyPufferModuleManager { - IRegistryCoordinatorExtended(avsRegistryCoordinator).registerOperatorWithChurn({ - quorumNumbers: quorumNumbers, - socket: socket, - params: params, - operatorKickParams: operatorKickParams, - churnApproverSignature: churnApproverSignature, - operatorSignature: operatorSignature - }); - } - - /** - * @inheritdoc IRestakingOperator - * @dev Restricted to the PufferModuleManager - */ - function customCalldataCall(address target, bytes calldata customCalldata) + function registerOperatorToAVS(IAllocationManager.RegisterParams calldata registrationParams) external virtual onlyPufferModuleManager - returns (bytes memory response) { - return target.functionCall(customCalldata); + EIGEN_ALLOCATION_MANAGER.registerForOperatorSets(address(this), registrationParams); } /** - * @inheritdoc IRestakingOperator * @dev Restricted to the PufferModuleManager */ - function deregisterOperatorFromAVS(address avsRegistryCoordinator, bytes calldata quorumNumbers) + function deregisterOperatorFromAVS(IAllocationManager.DeregisterParams calldata deregistrationParams) external virtual onlyPufferModuleManager { - IRegistryCoordinatorExtended(avsRegistryCoordinator).deregisterOperator(quorumNumbers); + EIGEN_ALLOCATION_MANAGER.deregisterFromOperatorSets(deregistrationParams); } /** - * @inheritdoc IRestakingOperator * @dev Restricted to the PufferModuleManager */ - function updateOperatorAVSSocket(address avsRegistryCoordinator, string calldata socket) + function customCalldataCall(address target, bytes calldata customCalldata) external virtual onlyPufferModuleManager + returns (bytes memory response) { - IRegistryCoordinatorExtended(avsRegistryCoordinator).updateSocket(socket); + return target.functionCall(customCalldata); } /** - * @inheritdoc IRestakingOperator * @dev Restricted to PufferModuleManager */ function callSetClaimerFor(address claimer) external virtual onlyPufferModuleManager { diff --git a/mainnet-contracts/src/interface/EigenLayer/IDelegationManager.sol b/mainnet-contracts/src/interface/EigenLayer/IDelegationManager.sol deleted file mode 100644 index 9d176a12..00000000 --- a/mainnet-contracts/src/interface/EigenLayer/IDelegationManager.sol +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IStrategy } from "./IStrategy.sol"; - -interface IDelegationManager { - // @notice Struct used for storing information about a single operator who has registered with EigenLayer - struct OperatorDetails { - // @notice address to receive the rewards that the operator earns via serving applications built on EigenLayer. - address earningsReceiver; - /** - * @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations". - * @dev Signature verification follows these rules: - * 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed. - * 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator. - * 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value". - */ - address delegationApprover; - /** - * @notice A minimum delay -- measured in blocks -- enforced between: - * 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing` - * and - * 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate` - * @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails, - * then they are only allowed to either increase this value or keep it the same. - */ - uint32 stakerOptOutWindowBlocks; - } - - /** - * @notice Abstract struct used in calculating an EIP712 signature for a staker to approve that they (the staker themselves) delegate to a specific operator. - * @dev Used in computing the `STAKER_DELEGATION_TYPEHASH` and as a reference in the computation of the stakerDigestHash in the `delegateToBySignature` function. - */ - struct StakerDelegation { - // the staker who is delegating - address staker; - // the operator being delegated to - address operator; - // the staker's nonce - uint256 nonce; - // the expiration timestamp (UTC) of the signature - uint256 expiry; - } - - /** - * @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator. - * @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function. - */ - struct DelegationApproval { - // the staker who is delegating - address staker; - // the operator being delegated to - address operator; - // the operator's provided salt - bytes32 salt; - // the expiration timestamp (UTC) of the signature - uint256 expiry; - } - - /** - * Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored. - * In functions that operate on existing queued withdrawals -- e.g. completeQueuedWithdrawal`, the data is resubmitted and the hash of the submitted - * data is computed by `calculateWithdrawalRoot` and checked against the stored hash in order to confirm the integrity of the submitted data. - */ - struct Withdrawal { - // The address that originated the Withdrawal - address staker; - // The address that the staker was delegated to at the time that the Withdrawal was created - address delegatedTo; - // The address that can complete the Withdrawal + will receive funds when completing the withdrawal - address withdrawer; - // Nonce used to guarantee that otherwise identical withdrawals have unique hashes - uint256 nonce; - // Block number when the Withdrawal was created - uint32 startBlock; - // Array of strategies that the Withdrawal contains - IStrategy[] strategies; - // Array containing the amount of shares in each Strategy in the `strategies` array - uint256[] shares; - } - - struct QueuedWithdrawalParams { - // Array of strategies that the QueuedWithdrawal contains - IStrategy[] strategies; - // Array containing the amount of shares in each Strategy in the `strategies` array - uint256[] shares; - // The address of the withdrawer - address withdrawer; - } - - // @notice Emitted when a new operator registers in EigenLayer and provides their OperatorDetails. - event OperatorRegistered(address indexed operator, OperatorDetails operatorDetails); - - /// @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails - event OperatorDetailsModified(address indexed operator, OperatorDetails newOperatorDetails); - - /** - * @notice Emitted when @param operator indicates that they are updating their MetadataURI string - * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing - */ - event OperatorMetadataURIUpdated(address indexed operator, string metadataURI); - - /// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares. - event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); - - /// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares. - event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); - - /// @notice Emitted when @param staker delegates to @param operator. - event StakerDelegated(address indexed staker, address indexed operator); - - /// @notice Emitted when @param staker undelegates from @param operator. - event StakerUndelegated(address indexed staker, address indexed operator); - - /// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself - event StakerForceUndelegated(address indexed staker, address indexed operator); - - /** - * @notice Emitted when a new withdrawal is queued. - * @param withdrawalRoot Is the hash of the `withdrawal`. - * @param withdrawal Is the withdrawal itself. - */ - event WithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal); - - /// @notice Emitted when a queued withdrawal is completed - event WithdrawalCompleted(bytes32 withdrawalRoot); - - /// @notice Emitted when a queued withdrawal is *migrated* from the StrategyManager to the DelegationManager - event WithdrawalMigrated(bytes32 oldWithdrawalRoot, bytes32 newWithdrawalRoot); - - /// @notice Emitted when the `minWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`. - event MinWithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue); - - /// @notice Emitted when the `strategyWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`. - event StrategyWithdrawalDelayBlocksSet(IStrategy strategy, uint256 previousValue, uint256 newValue); - - /** - * Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed - * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from - * their operator. - * - * All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay. - */ - function queueWithdrawals(QueuedWithdrawalParams[] calldata queuedWithdrawalParams) - external - returns (bytes32[] memory); - - /** - * @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer` - * @param withdrawal The Withdrawal to complete. - * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array. - * This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused) - * @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array - * @param receiveAsTokens If true, the shares specified in the withdrawal will be withdrawn from the specified strategies themselves - * and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies - * will simply be transferred to the caller directly. - * @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw` - * @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that - * any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in - * any other strategies, which will be transferred to the withdrawer. - */ - function completeQueuedWithdrawal( - Withdrawal calldata withdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex, - bool receiveAsTokens - ) external; - - /** - * @notice Array-ified version of `completeQueuedWithdrawal`. - * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` - * @param withdrawals The Withdrawals to complete. - * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. - * @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index. - * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. - * @dev See `completeQueuedWithdrawal` for relevant dev tags - */ - function completeQueuedWithdrawals( - Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, - bool[] calldata receiveAsTokens - ) external; - - /// @notice Returns the keccak256 hash of `withdrawal`. - function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32); -} diff --git a/mainnet-contracts/src/interface/EigenLayer/IRewardsCoordinator.sol b/mainnet-contracts/src/interface/EigenLayer/IRewardsCoordinator.sol deleted file mode 100644 index 71985151..00000000 --- a/mainnet-contracts/src/interface/EigenLayer/IRewardsCoordinator.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -/** - * @title RewardsCoordinator - * @author Eigen Labs Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice This is the contract for rewards in EigenLayer. The main functionalities of this contract are - * - enabling any ERC20 rewards from AVSs to their operators and stakers for a given time range - * - allowing stakers and operators to claim their earnings including a commission bips for operators - * - allowing the protocol to provide ERC20 tokens to stakers over a specified time range - */ -interface IRewardsCoordinator { - /** - * @notice Sets the address of the entity that can call `processClaim` on behalf of the earner (msg.sender) - * @param claimer The address of the entity that can call `processClaim` on behalf of the earner - * @dev Only callable by the `earner` - */ - function setClaimerFor(address claimer) external; -} diff --git a/mainnet-contracts/src/interface/EigenLayer/IStrategy.sol b/mainnet-contracts/src/interface/EigenLayer/IStrategy.sol deleted file mode 100644 index 773a699c..00000000 --- a/mainnet-contracts/src/interface/EigenLayer/IStrategy.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -interface IStrategy { - /** - * @notice Returns the amount of underlying tokens for `user` - */ - function userUnderlyingView(address user) external view returns (uint256); - - /** - * @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy. - * @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications - * @param amountShares is the amount of shares to calculate its conversion into the underlying token - * @return The amount of shares corresponding to the input `amountUnderlying` - * @dev Implementation for these functions in particular may vary significantly for different strategies - */ - function sharesToUnderlyingView(uint256 amountShares) external view returns (uint256); -} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol new file mode 100644 index 00000000..4fd2ca05 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "./ISignatureUtils.sol"; +import "./IPauserRegistry.sol"; +import "./IStrategy.sol"; + +interface IAVSDirectoryErrors { + /// Operator Status + + /// @dev Thrown when an operator does not exist in the DelegationManager + error OperatorNotRegisteredToEigenLayer(); + /// @dev Thrown when an operator is already registered to an AVS. + error OperatorNotRegisteredToAVS(); + /// @dev Thrown when `operator` is already registered to the AVS. + error OperatorAlreadyRegisteredToAVS(); + /// @dev Thrown when attempting to spend a spent eip-712 salt. + error SaltSpent(); +} + +interface IAVSDirectoryTypes { + /// @notice Enum representing the registration status of an operator with an AVS. + /// @notice Only used by legacy M2 AVSs that have not integrated with operatorSets. + enum OperatorAVSRegistrationStatus { + UNREGISTERED, // Operator not registered to AVS + REGISTERED // Operator registered to AVS + + } + + /** + * @notice Struct representing the registration status of an operator with an operator set. + * Keeps track of last deregistered timestamp for slashability concerns. + * @param registered whether the operator is registered with the operator set + * @param lastDeregisteredTimestamp the timestamp at which the operator was last deregistered + */ + struct OperatorSetRegistrationStatus { + bool registered; + uint32 lastDeregisteredTimestamp; + } +} + +interface IAVSDirectoryEvents is IAVSDirectoryTypes { + /** + * @notice Emitted when an operator's registration status with an AVS id udpated + * @notice Only used by legacy M2 AVSs that have not integrated with operatorSets. + */ + event OperatorAVSRegistrationStatusUpdated( + address indexed operator, address indexed avs, OperatorAVSRegistrationStatus status + ); + + /// @notice Emitted when an AVS updates their metadata URI (Uniform Resource Identifier). + /// @dev The URI is never stored; it is simply emitted through an event for off-chain indexing. + event AVSMetadataURIUpdated(address indexed avs, string metadataURI); + + /// @notice Emitted when an AVS migrates to using operator sets. + event AVSMigratedToOperatorSets(address indexed avs); + + /// @notice Emitted when an operator is migrated from M2 registration to operator sets. + event OperatorMigratedToOperatorSets(address indexed operator, address indexed avs, uint32[] operatorSetIds); +} + +interface IAVSDirectory is IAVSDirectoryEvents, IAVSDirectoryErrors, ISignatureUtils { + /** + * + * EXTERNAL FUNCTIONS + * + */ + + /** + * @dev Initializes the addresses of the initial owner and paused status. + */ + function initialize(address initialOwner, uint256 initialPausedStatus) external; + + /** + * @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated. + * + * @param metadataURI The URI for metadata associated with an AVS. + * + * @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event. + */ + function updateAVSMetadataURI(string calldata metadataURI) external; + + /** + * @notice Called by an operator to cancel a salt that has been used to register with an AVS. + * + * @param salt A unique and single use value associated with the approver signature. + */ + function cancelSalt(bytes32 salt) external; + + /** + * @notice Legacy function called by the AVS's service manager contract + * to register an operator with the AVS. NOTE: this function will be deprecated in a future release + * after the slashing release. New AVSs should use `registerForOperatorSets` instead. + * + * @param operator The address of the operator to register. + * @param operatorSignature The signature, salt, and expiry of the operator's signature. + * + * @dev msg.sender must be the AVS. + * @dev Only used by legacy M2 AVSs that have not integrated with operator sets. + */ + function registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external; + + /** + * @notice Legacy function called by an AVS to deregister an operator from the AVS. + * NOTE: this function will be deprecated in a future release after the slashing release. + * New AVSs integrating should use `deregisterOperatorFromOperatorSets` instead. + * + * @param operator The address of the operator to deregister. + * + * @dev Only used by legacy M2 AVSs that have not integrated with operator sets. + */ + function deregisterOperatorFromAVS(address operator) external; + + /** + * + * VIEW FUNCTIONS + * + */ + function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool); + + /** + * @notice Calculates the digest hash to be signed by an operator to register with an AVS. + * + * @param operator The account registering as an operator. + * @param avs The AVS the operator is registering with. + * @param salt A unique and single-use value associated with the approver's signature. + * @param expiry The time after which the approver's signature becomes invalid. + */ + function calculateOperatorAVSRegistrationDigestHash(address operator, address avs, bytes32 salt, uint256 expiry) + external + view + returns (bytes32); + + /// @notice The EIP-712 typehash for the Registration struct used by the contract. + function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32); + + /// @notice The EIP-712 typehash for the OperatorSetRegistration struct used by the contract. + function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSRegistrar.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSRegistrar.sol new file mode 100644 index 00000000..9cc7f32f --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSRegistrar.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +interface IAVSRegistrar { + /** + * @notice Called by the AllocationManager when an operator wants to register + * for one or more operator sets. This method should revert if registration + * is unsuccessful. + * @param operator the registering operator + * @param operatorSetIds the list of operator set ids being registered for + * @param data arbitrary data the operator can provide as part of registration + */ + function registerOperator(address operator, uint32[] calldata operatorSetIds, bytes calldata data) external; + + /** + * @notice Called by the AllocationManager when an operator is deregistered from + * one or more operator sets. If this method reverts, it is ignored. + * @param operator the deregistering operator + * @param operatorSetIds the list of operator set ids being deregistered from + */ + function deregisterOperator(address operator, uint32[] calldata operatorSetIds) external; +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAllocationManager.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAllocationManager.sol new file mode 100644 index 00000000..fd507535 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAllocationManager.sol @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import { OperatorSet } from "../libraries/OperatorSetLib.sol"; +import "./IPauserRegistry.sol"; +import "./IStrategy.sol"; +import "./ISignatureUtils.sol"; +import "./IAVSRegistrar.sol"; + +interface IAllocationManagerErrors { + /// Input Validation + + /// @dev Thrown when `wadToSlash` is zero or greater than 1e18 + error InvalidWadToSlash(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when creating an operator set with more than max strategies. + error MaxStrategiesExceeded(); + + /// Caller + + /// @dev Thrown when caller is not authorized to call a function. + error InvalidCaller(); + + /// Operator Status + + /// @dev Thrown when an invalid operator is provided. + error InvalidOperator(); + /// @dev Thrown when an operator's allocation delay has yet to be set. + error UninitializedAllocationDelay(); + /// @dev Thrown when attempting to slash an operator when they are not slashable. + error OperatorNotSlashable(); + /// @dev Thrown when trying to add an operator to a set they are already a member of + error AlreadyMemberOfSet(); + /// @dev Thrown when trying to slash/remove an operator from a set they are not a member of + error NotMemberOfSet(); + + /// Operator Set Status + + /// @dev Thrown when an invalid operator set is provided. + error InvalidOperatorSet(); + /// @dev Thrown when provided `strategies` are not in ascending order. + error StrategiesMustBeInAscendingOrder(); + /// @dev Thrown when trying to add a strategy to an operator set that already contains it. + error StrategyAlreadyInOperatorSet(); + /// @dev Thrown when a strategy is referenced that does not belong to an operator set. + error StrategyNotInOperatorSet(); + + /// Modifying Allocations + + /// @dev Thrown when an operator attempts to set their allocation for an operatorSet to the same value + error SameMagnitude(); + /// @dev Thrown when an allocation is attempted for a given operator when they have pending allocations or deallocations. + error ModificationAlreadyPending(); + /// @dev Thrown when an allocation is attempted that exceeds a given operators total allocatable magnitude. + error InsufficientMagnitude(); +} + +interface IAllocationManagerTypes { + /** + * @notice Defines allocation information from a strategy to an operator set, for an operator + * @param currentMagnitude the current magnitude allocated from the strategy to the operator set + * @param pendingDiff a pending change in magnitude, if it exists (0 otherwise) + * @param effectBlock the block at which the pending magnitude diff will take effect + */ + struct Allocation { + uint64 currentMagnitude; + int128 pendingDiff; + uint32 effectBlock; + } + + /** + * @notice Struct containing allocation delay metadata for a given operator. + * @param delay Current allocation delay + * @param isSet Whether the operator has initially set an allocation delay. Note that this could be false but the + * block.number >= effectBlock in which we consider their delay to be configured and active. + * @param pendingDelay The delay that will take effect after `effectBlock` + * @param effectBlock The block number after which a pending delay will take effect + */ + struct AllocationDelayInfo { + uint32 delay; + bool isSet; + uint32 pendingDelay; + uint32 effectBlock; + } + + /** + * @notice Contains registration details for an operator pertaining to an operator set + * @param registered Whether the operator is currently registered for the operator set + * @param slashableUntil If the operator is not registered, how long until the operator is no longer + * slashable by the AVS. + */ + struct RegistrationStatus { + bool registered; + uint32 slashableUntil; + } + + /** + * @notice Contains allocation info for a specific strategy + * @param maxMagnitude the maximum magnitude that can be allocated between all operator sets + * @param encumberedMagnitude the currently-allocated magnitude for the strategy + */ + struct StrategyInfo { + uint64 maxMagnitude; + uint64 encumberedMagnitude; + } + + /** + * @notice Struct containing parameters to slashing + * @param operator the address to slash + * @param operatorSetId the ID of the operatorSet the operator is being slashed on behalf of + * @param strategies the set of strategies to slash + * @param wadsToSlash the parts in 1e18 to slash, this will be proportional to the operator's + * slashable stake allocation for the operatorSet + * @param description the description of the slashing provided by the AVS for legibility + */ + struct SlashingParams { + address operator; + uint32 operatorSetId; + IStrategy[] strategies; + uint256[] wadsToSlash; + string description; + } + + /** + * @notice struct used to modify the allocation of slashable magnitude to an operator set + * @param operatorSet the operator set to modify the allocation for + * @param strategies the strategies to modify allocations for + * @param newMagnitudes the new magnitude to allocate for each strategy to this operator set + */ + struct AllocateParams { + OperatorSet operatorSet; + IStrategy[] strategies; + uint64[] newMagnitudes; + } + + /** + * @notice Parameters used to register for an AVS's operator sets + * @param avs the AVS being registered for + * @param operatorSetIds the operator sets within the AVS to register for + * @param data extra data to be passed to the AVS to complete registration + */ + struct RegisterParams { + address avs; + uint32[] operatorSetIds; + bytes data; + } + + /** + * @notice Parameters used to deregister from an AVS's operator sets + * @param operator the operator being deregistered + * @param avs the avs being deregistered from + * @param operatorSetIds the operator sets within the AVS being deregistered from + */ + struct DeregisterParams { + address operator; + address avs; + uint32[] operatorSetIds; + } + + /** + * @notice Parameters used by an AVS to create new operator sets + * @param operatorSetId the id of the operator set to create + * @param strategies the strategies to add as slashable to the operator set + */ + struct CreateSetParams { + uint32 operatorSetId; + IStrategy[] strategies; + } +} + +interface IAllocationManagerEvents is IAllocationManagerTypes { + /// @notice Emitted when operator updates their allocation delay. + event AllocationDelaySet(address operator, uint32 delay, uint32 effectBlock); + + /// @notice Emitted when an operator's magnitude is updated for a given operatorSet and strategy + event AllocationUpdated( + address operator, OperatorSet operatorSet, IStrategy strategy, uint64 magnitude, uint32 effectBlock + ); + + /// @notice Emitted when operator's encumbered magnitude is updated for a given strategy + event EncumberedMagnitudeUpdated(address operator, IStrategy strategy, uint64 encumberedMagnitude); + + /// @notice Emitted when an operator's max magnitude is updated for a given strategy + event MaxMagnitudeUpdated(address operator, IStrategy strategy, uint64 maxMagnitude); + + /// @notice Emitted when an operator is slashed by an operator set for a strategy + /// `wadSlashed` is the proportion of the operator's total delegated stake that was slashed + event OperatorSlashed( + address operator, OperatorSet operatorSet, IStrategy[] strategies, uint256[] wadSlashed, string description + ); + + /// @notice Emitted when an AVS configures the address that will handle registration/deregistration + event AVSRegistrarSet(address avs, IAVSRegistrar registrar); + + /// @notice Emitted when an AVS updates their metadata URI (Uniform Resource Identifier). + /// @dev The URI is never stored; it is simply emitted through an event for off-chain indexing. + event AVSMetadataURIUpdated(address indexed avs, string metadataURI); + + /// @notice Emitted when an operator set is created by an AVS. + event OperatorSetCreated(OperatorSet operatorSet); + + /// @notice Emitted when an operator is added to an operator set. + event OperatorAddedToOperatorSet(address indexed operator, OperatorSet operatorSet); + + /// @notice Emitted when an operator is removed from an operator set. + event OperatorRemovedFromOperatorSet(address indexed operator, OperatorSet operatorSet); + + /// @notice Emitted when a strategy is added to an operator set. + event StrategyAddedToOperatorSet(OperatorSet operatorSet, IStrategy strategy); + + /// @notice Emitted when a strategy is removed from an operator set. + event StrategyRemovedFromOperatorSet(OperatorSet operatorSet, IStrategy strategy); +} + +interface IAllocationManager is ISignatureUtils, IAllocationManagerErrors, IAllocationManagerEvents { + /** + * @dev Initializes the initial owner and paused status. + */ + function initialize(address initialOwner, uint256 initialPausedStatus) external; + + /** + * @notice Called by an AVS to slash an operator in a given operator set + */ + function slashOperator(address avs, SlashingParams calldata params) external; + + /** + * @notice Modifies the proportions of slashable stake allocated to an operator set from a list of strategies + * Note that deallocations remain slashable for DEALLOCATION_DELAY blocks therefore when they are cleared they may + * free up less allocatable magnitude than initially deallocated. + * @param operator the operator to modify allocations for + * @param params array of magnitude adjustments for one or more operator sets + * @dev Updates encumberedMagnitude for the updated strategies + * @dev msg.sender is used as operator + */ + function modifyAllocations(address operator, AllocateParams[] calldata params) external; + + /** + * @notice This function takes a list of strategies and for each strategy, removes from the deallocationQueue + * all clearable deallocations up to max `numToClear` number of deallocations, updating the encumberedMagnitude + * of the operator as needed. + * + * @param operator address to clear deallocations for + * @param strategies a list of strategies to clear deallocations for + * @param numToClear a list of number of pending deallocations to clear for each strategy + * + * @dev can be called permissionlessly by anyone + */ + function clearDeallocationQueue(address operator, IStrategy[] calldata strategies, uint16[] calldata numToClear) + external; + + /** + * @notice Allows an operator to register for one or more operator sets for an AVS. If the operator + * has any stake allocated to these operator sets, it immediately becomes slashable. + * @dev After registering within the ALM, this method calls `avs.registerOperator` to complete + * registration. This call MUST succeed in order for registration to be successful. + */ + function registerForOperatorSets(address operator, RegisterParams calldata params) external; + + /** + * @notice Allows an operator or AVS to deregister the operator from one or more of the AVS's operator sets. + * If the operator has any slashable stake allocated to the AVS, it remains slashable until the + * DEALLOCATION_DELAY has passed. + * @dev After deregistering within the ALM, this method calls `avs.deregisterOperator` to complete + * deregistration. If this call reverts, it is ignored. + */ + function deregisterFromOperatorSets(DeregisterParams calldata params) external; + + /** + * @notice Called by the delegation manager OR an operator to set an operator's allocation delay. + * This is set when the operator first registers, and is the number of blocks between an operator + * allocating magnitude to an operator set, and the magnitude becoming slashable. + * @param operator The operator to set the delay on behalf of. + * @param delay the allocation delay in blocks + */ + function setAllocationDelay(address operator, uint32 delay) external; + + /** + * @notice Called by an AVS to configure the address that is called when an operator registers + * or is deregistered from the AVS's operator sets. If not set (or set to 0), defaults + * to the AVS's address. + * @param registrar the new registrar address + */ + function setAVSRegistrar(address avs, IAVSRegistrar registrar) external; + + /** + * @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated. + * + * @param metadataURI The URI for metadata associated with an AVS. + * + * @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event. + */ + function updateAVSMetadataURI(address avs, string calldata metadataURI) external; + + /** + * @notice Allows an AVS to create new operator sets, defining strategies that the operator set uses + */ + function createOperatorSets(address avs, CreateSetParams[] calldata params) external; + + /** + * @notice Allows an AVS to add strategies to an operator set + * @dev Strategies MUST NOT already exist in the operator set + * @param avs the avs to set strategies for + * @param operatorSetId the operator set to add strategies to + * @param strategies the strategies to add + */ + function addStrategiesToOperatorSet(address avs, uint32 operatorSetId, IStrategy[] calldata strategies) external; + + /** + * @notice Allows an AVS to remove strategies from an operator set + * @dev Strategies MUST already exist in the operator set + * @param avs the avs to remove strategies for + * @param operatorSetId the operator set to remove strategies from + * @param strategies the strategies to remove + */ + function removeStrategiesFromOperatorSet(address avs, uint32 operatorSetId, IStrategy[] calldata strategies) + external; + + /** + * + * VIEW FUNCTIONS + * + */ + + /** + * @notice Returns the number of operator sets for the AVS + * @param avs the AVS to query + */ + function getOperatorSetCount(address avs) external view returns (uint256); + + /** + * @notice Returns the list of operator sets the operator has current or pending allocations/deallocations in + * @param operator the operator to query + * @return the list of operator sets the operator has current or pending allocations/deallocations in + */ + function getAllocatedSets(address operator) external view returns (OperatorSet[] memory); + + /** + * @notice Returns the list of strategies an operator has current or pending allocations/deallocations from + * given a specific operator set. + * @param operator the operator to query + * @param operatorSet the operator set to query + * @return the list of strategies + */ + function getAllocatedStrategies(address operator, OperatorSet memory operatorSet) + external + view + returns (IStrategy[] memory); + + /** + * @notice Returns the current/pending stake allocation an operator has from a strategy to an operator set + * @param operator the operator to query + * @param operatorSet the operator set to query + * @param strategy the strategy to query + * @return the current/pending stake allocation + */ + function getAllocation(address operator, OperatorSet memory operatorSet, IStrategy strategy) + external + view + returns (Allocation memory); + + /** + * @notice Returns the current/pending stake allocations for multiple operators from a strategy to an operator set + * @param operators the operators to query + * @param operatorSet the operator set to query + * @param strategy the strategy to query + * @return each operator's allocation + */ + function getAllocations(address[] memory operators, OperatorSet memory operatorSet, IStrategy strategy) + external + view + returns (Allocation[] memory); + + /** + * @notice Given a strategy, returns a list of operator sets and corresponding stake allocations. + * @dev Note that this returns a list of ALL operator sets the operator has allocations in. This means + * some of the returned allocations may be zero. + * @param operator the operator to query + * @param strategy the strategy to query + * @return the list of all operator sets the operator has allocations for + * @return the corresponding list of allocations from the specific `strategy` + */ + function getStrategyAllocations(address operator, IStrategy strategy) + external + view + returns (OperatorSet[] memory, Allocation[] memory); + + /** + * @notice For a strategy, get the amount of magnitude not currently allocated to any operator set + * @param operator the operator to query + * @param strategy the strategy to get allocatable magnitude for + * @return magnitude available to be allocated to an operator set + */ + function getAllocatableMagnitude(address operator, IStrategy strategy) external view returns (uint64); + + /** + * @notice Returns the maximum magnitude an operator can allocate for the given strategy + * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime + * the operator is slashed. This value acts as a cap on the max magnitude of the operator. + * @param operator the operator to query + * @param strategy the strategy to get the max magnitude for + * @return the max magnitude for the strategy + */ + function getMaxMagnitude(address operator, IStrategy strategy) external view returns (uint64); + + /** + * @notice Returns the maximum magnitude an operator can allocate for the given strategies + * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime + * the operator is slashed. This value acts as a cap on the max magnitude of the operator. + * @param operator the operator to query + * @param strategies the strategies to get the max magnitudes for + * @return the max magnitudes for each strategy + */ + function getMaxMagnitudes(address operator, IStrategy[] calldata strategies) + external + view + returns (uint64[] memory); + + /** + * @notice Returns the maximum magnitudes each operator can allocate for the given strategy + * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime + * the operator is slashed. This value acts as a cap on the max magnitude of the operator. + * @param operators the operators to query + * @param strategy the strategy to get the max magnitudes for + * @return the max magnitudes for each operator + */ + function getMaxMagnitudes(address[] calldata operators, IStrategy strategy) + external + view + returns (uint64[] memory); + + /** + * @notice Returns the maximum magnitude an operator can allocate for the given strategies + * at a given block number + * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime + * the operator is slashed. This value acts as a cap on the max magnitude of the operator. + * @param operator the operator to query + * @param strategies the strategies to get the max magnitudes for + * @param blockNumber the blockNumber at which to check the max magnitudes + * @return the max magnitudes for each strategy + */ + function getMaxMagnitudesAtBlock(address operator, IStrategy[] calldata strategies, uint32 blockNumber) + external + view + returns (uint64[] memory); + + /** + * @notice Returns the time in blocks between an operator allocating slashable magnitude + * and the magnitude becoming slashable. If the delay has not been set, `isSet` will be false. + * @dev The operator must have a configured delay before allocating magnitude + * @param operator The operator to query + * @return isSet Whether the operator has configured a delay + * @return delay The time in blocks between allocating magnitude and magnitude becoming slashable + */ + function getAllocationDelay(address operator) external view returns (bool isSet, uint32 delay); + + /** + * @notice Returns a list of all operator sets the operator is registered for + * @param operator The operator address to query. + */ + function getRegisteredSets(address operator) external view returns (OperatorSet[] memory operatorSets); + + /** + * @notice Returns whether the operator is registered for the operator set + * @param operator The operator to query + * @param operatorSet The operator set to query + */ + function isMemberOfOperatorSet(address operator, OperatorSet memory operatorSet) external view returns (bool); + + /** + * @notice Returns whether the operator set exists + */ + function isOperatorSet(OperatorSet memory operatorSet) external view returns (bool); + + /** + * @notice Returns all the operators registered to an operator set + * @param operatorSet The operatorSet to query. + */ + function getMembers(OperatorSet memory operatorSet) external view returns (address[] memory operators); + + /** + * @notice Returns the number of operators registered to an operatorSet. + * @param operatorSet The operatorSet to get the member count for + */ + function getMemberCount(OperatorSet memory operatorSet) external view returns (uint256); + + /** + * @notice Returns the address that handles registration/deregistration for the AVS + * If not set, defaults to the input address (`avs`) + */ + function getAVSRegistrar(address avs) external view returns (IAVSRegistrar); + + /** + * @notice Returns an array of strategies in the operatorSet. + * @param operatorSet The operatorSet to query. + */ + function getStrategiesInOperatorSet(OperatorSet memory operatorSet) + external + view + returns (IStrategy[] memory strategies); + + /** + * @notice Returns the minimum amount of stake that will be slashable as of some future block, + * according to each operator's allocation from each strategy to the operator set. + * @dev This method queries actual delegated stakes in the DelegationManager and applies + * each operator's allocation to the stake to produce the slashable stake each allocation + * represents. + * @dev This minimum takes into account `futureBlock`, and will omit any pending magnitude + * diffs that will not be in effect as of `futureBlock`. NOTE that in order to get the true + * minimum slashable stake as of some future block, `futureBlock` MUST be greater than block.number + * @dev NOTE that `futureBlock` should be fewer than `DEALLOCATION_DELAY` blocks in the future, + * or the values returned from this method may not be accurate due to deallocations. + * @param operatorSet the operator set to query + * @param operators the list of operators whose slashable stakes will be returned + * @param strategies the strategies that each slashable stake corresponds to + * @param futureBlock the block at which to get allocation information. Should be a future block. + * @return slashableStake a list of slashable stakes, indexed by [operator][strategy] + */ + function getMinimumSlashableStake( + OperatorSet memory operatorSet, + address[] memory operators, + IStrategy[] memory strategies, + uint32 futureBlock + ) external view returns (uint256[][] memory slashableStake); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IBackingEigen.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IBackingEigen.sol new file mode 100644 index 00000000..2542d734 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IBackingEigen.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IBackingEigen is IERC20 { + /** + * @notice This function allows the owner to set the allowedFrom status of an address + * @param from the address whose allowedFrom status is being set + * @param isAllowedFrom the new allowedFrom status + */ + function setAllowedFrom(address from, bool isAllowedFrom) external; + + /** + * @notice This function allows the owner to set the allowedTo status of an address + * @param to the address whose allowedTo status is being set + * @param isAllowedTo the new allowedTo status + */ + function setAllowedTo(address to, bool isAllowedTo) external; + + /** + * @notice Allows the owner to disable transfer restrictions + */ + function disableTransferRestrictions() external; + + /** + * @notice An initializer function that sets initial values for the contract's state variables. + */ + function initialize(address initialOwner) external; + + // @notice Allows the contract owner to modify an entry in the `isMinter` mapping. + function setIsMinter(address minterAddress, bool newStatus) external; + + /** + * @notice Allows any privileged address to mint `amount` new tokens to the address `to`. + * @dev Callable only by an address that has `isMinter` set to true. + */ + function mint(address to, uint256 amount) external; + + /** + * @dev Destroys `amount` tokens from the caller. + * + * See {ERC20-_burn}. + */ + function burn(uint256 amount) external; + + /// @notice the address of the wrapped Eigen token EIGEN + function EIGEN() external view returns (IERC20); + + /// @notice the timestamp after which transfer restrictions are disabled + function transferRestrictionsDisabledAfter() external view returns (uint256); + + /** + * @dev Clock used for flagging checkpoints. Has been overridden to implement timestamp based + * checkpoints (and voting). + */ + function clock() external view returns (uint48); + + /** + * @dev Machine-readable description of the clock as specified in EIP-6372. + * Has been overridden to inform callers that this contract uses timestamps instead of block numbers, to match `clock()` + */ + // solhint-disable-next-line func-name-mixedcase + function CLOCK_MODE() external pure returns (string memory); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol new file mode 100644 index 00000000..ff983815 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "./IStrategy.sol"; +import "./ISignatureUtils.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IDelegationManagerErrors { + /// @dev Thrown when caller is neither the StrategyManager or EigenPodManager contract. + error OnlyStrategyManagerOrEigenPodManager(); + /// @dev Thrown when msg.sender is not the EigenPodManager + error OnlyEigenPodManager(); + /// @dev Throw when msg.sender is not the AllocationManager + error OnlyAllocationManager(); + + /// Delegation Status + + /// @dev Thrown when an operator attempts to undelegate. + error OperatorsCannotUndelegate(); + /// @dev Thrown when an account is actively delegated. + error ActivelyDelegated(); + /// @dev Thrown when an account is not actively delegated. + error NotActivelyDelegated(); + /// @dev Thrown when `operator` is not a registered operator. + error OperatorNotRegistered(); + + /// Invalid Inputs + + /// @dev Thrown when attempting to execute an action that was not queued. + error WithdrawalNotQueued(); + /// @dev Thrown when caller cannot undelegate on behalf of a staker. + error CallerCannotUndelegate(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when input arrays length is zero. + error InputArrayLengthZero(); + + /// Slashing + + /// @dev Thrown when an operator has been fully slashed(maxMagnitude is 0) for a strategy. + /// or if the staker has had been natively slashed to the point of their beaconChainScalingFactor equalling 0. + error FullySlashed(); + + /// Signatures + + /// @dev Thrown when attempting to spend a spent eip-712 salt. + error SaltSpent(); + + /// Withdrawal Processing + + /// @dev Thrown when attempting to withdraw before delay has elapsed. + error WithdrawalDelayNotElapsed(); + /// @dev Thrown when a withdraw amount larger than max is attempted. + error WithdrawalExceedsMax(); + /// @dev Thrown when withdrawer is not the current caller. + error WithdrawerNotCaller(); + /// @dev Thrown when `withdrawer` is not staker. + error WithdrawerNotStaker(); +} + +interface IDelegationManagerTypes { + // @notice Struct used for storing information about a single operator who has registered with EigenLayer + struct OperatorDetails { + /// @notice DEPRECATED -- this field is no longer used, payments are handled in RewardsCoordinator.sol + address __deprecated_earningsReceiver; + /** + * @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations". + * @dev Signature verification follows these rules: + * 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed. + * 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator. + * 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value". + */ + address delegationApprover; + /// @notice DEPRECATED -- this field is no longer used. An analogous field is the `allocationDelay` stored in the AllocationManager + uint32 __deprecated_stakerOptOutWindowBlocks; + } + + /** + * @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator. + * @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function. + */ + struct DelegationApproval { + // the staker who is delegating + address staker; + // the operator being delegated to + address operator; + // the operator's provided salt + bytes32 salt; + // the expiration timestamp (UTC) of the signature + uint256 expiry; + } + + /** + * Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored. + * In functions that operate on existing queued withdrawals -- e.g. completeQueuedWithdrawal`, the data is resubmitted and the hash of the submitted + * data is computed by `calculateWithdrawalRoot` and checked against the stored hash in order to confirm the integrity of the submitted data. + */ + struct Withdrawal { + // The address that originated the Withdrawal + address staker; + // The address that the staker was delegated to at the time that the Withdrawal was created + address delegatedTo; + // The address that can complete the Withdrawal + will receive funds when completing the withdrawal + address withdrawer; + // Nonce used to guarantee that otherwise identical withdrawals have unique hashes + uint256 nonce; + // Blocknumber when the Withdrawal was created. + uint32 startBlock; + // Array of strategies that the Withdrawal contains + IStrategy[] strategies; + // Array containing the amount of staker's scaledShares for withdrawal in each Strategy in the `strategies` array + // Note that these scaledShares need to be multiplied by the operator's maxMagnitude and beaconChainScalingFactor at completion to include + // slashing occurring during the queue withdrawal delay. This is because scaledShares = sharesToWithdraw / (maxMagnitude * beaconChainScalingFactor) + // at queue time. beaconChainScalingFactor is simply equal to 1 if the strategy is not the beaconChainStrategy. + // To account for slashing, we later multiply scaledShares * maxMagnitude * beaconChainScalingFactor at the earliest possible completion time + // to get the withdrawn shares after applying slashing during the delay period. + uint256[] scaledShares; + } + + struct QueuedWithdrawalParams { + // Array of strategies that the QueuedWithdrawal contains + IStrategy[] strategies; + // Array containing the amount of depositShares for withdrawal in each Strategy in the `strategies` array + // Note that the actual shares received on completing withdrawal may be less than the depositShares if slashing occurred + uint256[] depositShares; + // The address of the withdrawer + address withdrawer; + } +} + +interface IDelegationManagerEvents is IDelegationManagerTypes { + // @notice Emitted when a new operator registers in EigenLayer and provides their delegation approver. + event OperatorRegistered(address indexed operator, address delegationApprover); + + /// @notice Emitted when an operator updates their delegation approver + event DelegationApproverUpdated(address indexed operator, address newDelegationApprover); + + /** + * @notice Emitted when @param operator indicates that they are updating their MetadataURI string + * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing + */ + event OperatorMetadataURIUpdated(address indexed operator, string metadataURI); + + /// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares. + event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); + + /// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares. + event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); + + /// @notice Emitted whenever an operator's shares are burned for a given strategy + event OperatorSharesBurned(address indexed operator, IStrategy strategy, uint256 shares); + + /// @notice Emitted when @param staker delegates to @param operator. + event StakerDelegated(address indexed staker, address indexed operator); + + /// @notice Emitted when @param staker undelegates from @param operator. + event StakerUndelegated(address indexed staker, address indexed operator); + + /// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself + event StakerForceUndelegated(address indexed staker, address indexed operator); + + /// @notice Emitted when a staker's depositScalingFactor is updated + event DepositScalingFactorUpdated(address staker, IStrategy strategy, uint256 newDepositScalingFactor); + + /** + * @notice Emitted when a new withdrawal is queued. + * @param withdrawalRoot Is the hash of the `withdrawal`. + * @param withdrawal Is the withdrawal itself. + * @param sharesToWithdraw Is an array of the expected shares that were queued for withdrawal corresponding to the strategies in the `withdrawal`. + */ + event SlashingWithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal, uint256[] sharesToWithdraw); + + /// @notice Emitted when a queued withdrawal is completed + event SlashingWithdrawalCompleted(bytes32 withdrawalRoot); +} + +/** + * @title DelegationManager + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice This is the contract for delegation in EigenLayer. The main functionalities of this contract are + * - enabling anyone to register as an operator in EigenLayer + * - allowing operators to specify parameters related to stakers who delegate to them + * - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time) + * - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager) + */ +interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDelegationManagerEvents { + /** + * @dev Initializes the initial owner and paused status. + */ + function initialize(address initialOwner, uint256 initialPausedStatus) external; + + /** + * @notice Registers the caller as an operator in EigenLayer. + * @param initDelegationApprover is an address that, if set, must provide a signature when stakers delegate + * to an operator. + * @param allocationDelay The delay before allocations take effect. + * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator. + * + * @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself". + * @dev This function will revert if the caller is already delegated to an operator. + * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event + */ + function registerAsOperator(address initDelegationApprover, uint32 allocationDelay, string calldata metadataURI) + external; + + /** + * @notice Updates an operator's stored `delegationApprover`. + * @param operator is the operator to update the delegationApprover for + * @param newDelegationApprover is the new delegationApprover for the operator + * + * @dev The caller must have previously registered as an operator in EigenLayer. + */ + function modifyOperatorDetails(address operator, address newDelegationApprover) external; + + /** + * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated. + * @param operator The operator to update metadata for + * @param metadataURI The URI for metadata associated with an operator + * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event + */ + function updateOperatorMetadataURI(address operator, string calldata metadataURI) external; + + /** + * @notice Caller delegates their stake to an operator. + * @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer. + * @param approverSignatureAndExpiry Verifies the operator approves of this delegation + * @param approverSalt A unique single use value tied to an individual signature. + * @dev The approverSignatureAndExpiry is used in the event that the operator's `delegationApprover` address is set to a non-zero value. + * @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input + * in this case to save on complexity + gas costs + * @dev If the staker delegating has shares in a strategy that the operator was slashed 100% for (the operator's maxMagnitude = 0), + * then delegation is blocked and will revert. + */ + function delegateTo(address operator, SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt) + external; + + /** + * @notice Undelegates the staker from the operator who they are delegated to. + * Queues withdrawals of all of the staker's withdrawable shares in the StrategyManager (to the staker) and/or EigenPodManager, if necessary. + * @param staker The account to be undelegated. + * @return withdrawalRoots The roots of the newly queued withdrawals, if a withdrawal was queued. Otherwise just bytes32(0). + * + * @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves. + * @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover" + * @dev Reverts if the `staker` is already undelegated. + */ + function undelegate(address staker) external returns (bytes32[] memory withdrawalRoots); + + /** + * @notice Undelegates the staker from their current operator, and redelegates to `newOperator` + * Queues a withdrawal for all of the staker's withdrawable shares. These shares will only be + * delegated to `newOperator` AFTER the withdrawal is completed. + * @dev This method acts like a call to `undelegate`, then `delegateTo` + * @param newOperator the new operator that will be delegated all assets + * @dev NOTE: the following 2 params are ONLY checked if `newOperator` has a `delegationApprover`. + * If not, they can be left empty. + * @param newOperatorApproverSig A signature from the operator's `delegationApprover` + * @param approverSalt A unique single use value tied to the approver's signature + */ + function redelegate(address newOperator, SignatureWithExpiry memory newOperatorApproverSig, bytes32 approverSalt) + external + returns (bytes32[] memory withdrawalRoots); + + /** + * @notice Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed + * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from + * their operator. + * + * All withdrawn shares/strategies are placed in a queue and can be withdrawn after a delay. Withdrawals + * are still subject to slashing during the delay period so the amount withdrawn on completion may actually be less + * than what was queued if slashing has occurred in that period. + * + * @dev To view what the staker is able to queue withdraw, see `getWithdrawableShares()` + */ + function queueWithdrawals(QueuedWithdrawalParams[] calldata params) external returns (bytes32[] memory); + + /** + * @notice Used to complete the all queued withdrawals. + * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` + * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. + * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. + * @param numToComplete The number of withdrawals to complete. This must be less than or equal to the number of queued withdrawals. + * @dev See `completeQueuedWithdrawal` for relevant dev tags + */ + function completeQueuedWithdrawals( + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens, + uint256 numToComplete + ) external; + + /** + * @notice Used to complete the lastest queued withdrawal. + * @param withdrawal The withdrawal to complete. + * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array. + * @param receiveAsTokens If true, the shares calculated to be withdrawn will be withdrawn from the specified strategies themselves + * and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies + * will simply be transferred to the caller directly. + * @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that + * any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in + * any other strategies, which will be transferred to the withdrawer. + */ + function completeQueuedWithdrawal(Withdrawal calldata withdrawal, IERC20[] calldata tokens, bool receiveAsTokens) + external; + + /** + * @notice Used to complete the all queued withdrawals. + * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` + * @param withdrawals Array of Withdrawals to complete. See `completeQueuedWithdrawal` for the usage of a single Withdrawal. + * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. + * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. + * @dev See `completeQueuedWithdrawal` for relevant dev tags + */ + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens + ) external; + + /** + * @notice Increases a staker's delegated share balance in a strategy. Note that before adding to operator shares, + * the delegated delegatedShares. The staker's depositScalingFactor is updated here. + * @param staker The address to increase the delegated shares for their operator. + * @param strategy The strategy in which to increase the delegated shares. + * @param prevDepositShares The number of deposit shares the staker already had in the strategy. This is the shares amount stored in the + * StrategyManager/EigenPodManager for the staker's shares. + * @param addedShares The number of shares added to the staker's shares in the strategy + * + * @dev *If the staker is actively delegated*, then increases the `staker`'s delegated delegatedShares in `strategy`. + * Otherwise does nothing. + * @dev If the operator was slashed 100% for the strategy (the operator's maxMagnitude = 0), then increasing delegated shares is blocked and will revert. + * @dev Callable only by the StrategyManager or EigenPodManager. + */ + function increaseDelegatedShares(address staker, IStrategy strategy, uint256 prevDepositShares, uint256 addedShares) + external; + + /** + * @notice If the staker is delegated, decreases its operator's shares in response to + * a decrease in balance in the beaconChainETHStrategy + * @param staker the staker whose operator's balance will be decreased + * @param curDepositShares the current deposit shares held by the staker + * @param beaconChainSlashingFactorDecrease the amount that the staker's beaconChainSlashingFactor has decreased by + * @dev Note: `beaconChainSlashingFactorDecrease` are assumed to ALWAYS be < 1 WAD. + * These invariants are maintained in the EigenPodManager. + */ + function decreaseDelegatedShares(address staker, uint256 curDepositShares, uint64 beaconChainSlashingFactorDecrease) + external; + + /** + * @notice Decreases the operators shares in storage after a slash and burns the corresponding Strategy shares + * by calling into the StrategyManager or EigenPodManager to burn the shares. + * @param operator The operator to decrease shares for + * @param strategy The strategy to decrease shares for + * @param prevMaxMagnitude the previous maxMagnitude of the operator + * @param newMaxMagnitude the new maxMagnitude of the operator + * @dev Callable only by the AllocationManager + * @dev Note: Assumes `prevMaxMagnitude <= newMaxMagnitude`. This invariant is maintained in + * the AllocationManager. + */ + function burnOperatorShares(address operator, IStrategy strategy, uint64 prevMaxMagnitude, uint64 newMaxMagnitude) + external; + + /** + * + * VIEW FUNCTIONS + * + */ + + /** + * @notice returns the address of the operator that `staker` is delegated to. + * @notice Mapping: staker => operator whom the staker is currently delegated to. + * @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator. + */ + function delegatedTo(address staker) external view returns (address); + + /** + * @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover. + * @dev Salts are used in the `delegateTo` function. Note that this function only processes the delegationApprover's + * signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`. + */ + function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool); + + /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated. + /// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes. + function cumulativeWithdrawalsQueued(address staker) external view returns (uint256); + + /** + * @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise. + */ + function isDelegated(address staker) external view returns (bool); + + /** + * @notice Returns true is an operator has previously registered for delegation. + */ + function isOperator(address operator) external view returns (bool); + + /** + * @notice Returns the delegationApprover account for an operator + */ + function delegationApprover(address operator) external view returns (address); + + /** + * @notice Returns the shares that an operator has delegated to them in a set of strategies + * @param operator the operator to get shares for + * @param strategies the strategies to get shares for + */ + function getOperatorShares(address operator, IStrategy[] memory strategies) + external + view + returns (uint256[] memory); + + /** + * @notice Returns the shares that a set of operators have delegated to them in a set of strategies + * @param operators the operators to get shares for + * @param strategies the strategies to get shares for + */ + function getOperatorsShares(address[] memory operators, IStrategy[] memory strategies) + external + view + returns (uint256[][] memory); + + /** + * @notice Returns amount of withdrawable shares from an operator for a strategy that is still in the queue + * and therefore slashable. Note that the *actual* slashable amount could be less than this value as this doesn't account + * for amounts that have already been slashed. This assumes that none of the shares have been slashed. + * @param operator the operator to get shares for + * @param strategy the strategy to get shares for + * @return the amount of shares that are slashable in the withdrawal queue for an operator and a strategy + */ + function getSlashableSharesInQueue(address operator, IStrategy strategy) external view returns (uint256); + + /** + * @notice Given a staker and a set of strategies, return the shares they can queue for withdrawal and the + * corresponding depositShares. + * This value depends on which operator the staker is delegated to. + * The shares amount returned is the actual amount of Strategy shares the staker would receive (subject + * to each strategy's underlying shares to token ratio). + */ + function getWithdrawableShares(address staker, IStrategy[] memory strategies) + external + view + returns (uint256[] memory withdrawableShares, uint256[] memory depositShares); + + /** + * @notice Returns the number of shares in storage for a staker and all their strategies + */ + function getDepositedShares(address staker) external view returns (IStrategy[] memory, uint256[] memory); + + /** + * @notice Returns the scaling factor applied to a staker's deposits for a given strategy + */ + function depositScalingFactor(address staker, IStrategy strategy) external view returns (uint256); + + /** + * @notice Returns the minimum withdrawal delay in blocks to pass for withdrawals queued to be completable. + * Also applies to legacy withdrawals so any withdrawals not completed prior to the slashing upgrade will be subject + * to this longer delay. + */ + function MIN_WITHDRAWAL_DELAY_BLOCKS() external view returns (uint32); + + /// @notice Returns a list of pending queued withdrawals for a `staker`, and the `shares` to be withdrawn. + function getQueuedWithdrawals(address staker) + external + view + returns (Withdrawal[] memory withdrawals, uint256[][] memory shares); + + /// @notice Returns the keccak256 hash of `withdrawal`. + function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32); + + /** + * @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` function. + * @param staker The account delegating their stake + * @param operator The account receiving delegated stake + * @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general) + * @param approverSalt A unique and single use value associated with the approver signature. + * @param expiry Time after which the approver's signature becomes invalid + */ + function calculateDelegationApprovalDigestHash( + address staker, + address operator, + address _delegationApprover, + bytes32 approverSalt, + uint256 expiry + ) external view returns (bytes32); + + /// @notice return address of the beaconChainETHStrategy + function beaconChainETHStrategy() external view returns (IStrategy); + + /// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract + function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IETHPOSDeposit.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IETHPOSDeposit.sol new file mode 100644 index 00000000..5fc09a5c --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IETHPOSDeposit.sol @@ -0,0 +1,41 @@ +// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━ +// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓ +// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛ +// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━ +// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓ +// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛ +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity >=0.5.0; + +// This interface is designed to be compatible with the Vyper version. +/// @notice This is the Ethereum 2.0 deposit contract interface. +/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs +interface IETHPOSDeposit { + /// @notice A processed deposit event. + event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index); + + /// @notice Submit a Phase 0 DepositData object. + /// @param pubkey A BLS12-381 public key. + /// @param withdrawal_credentials Commitment to a public key for withdrawals. + /// @param signature A BLS12-381 signature. + /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object. + /// Used as a protection against malformed input. + function deposit( + bytes calldata pubkey, + bytes calldata withdrawal_credentials, + bytes calldata signature, + bytes32 deposit_data_root + ) external payable; + + /// @notice Query the current deposit root hash. + /// @return The deposit root hash. + function get_deposit_root() external view returns (bytes32); + + /// @notice Query the current deposit count. + /// @return The deposit count encoded as a little endian 64-bit number. + function get_deposit_count() external view returns (bytes memory); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigen.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigen.sol new file mode 100644 index 00000000..cd9279e5 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigen.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IEigen is IERC20 { + /** + * @notice This function allows the owner to set the allowedFrom status of an address + * @param from the address whose allowedFrom status is being set + * @param isAllowedFrom the new allowedFrom status + */ + function setAllowedFrom(address from, bool isAllowedFrom) external; + + /** + * @notice This function allows the owner to set the allowedTo status of an address + * @param to the address whose allowedTo status is being set + * @param isAllowedTo the new allowedTo status + */ + function setAllowedTo(address to, bool isAllowedTo) external; + + /** + * @notice Allows the owner to disable transfer restrictions + */ + function disableTransferRestrictions() external; + + /** + * @notice This function allows minter to mint tokens + */ + function mint() external; + + /** + * @notice This function allows bEIGEN holders to wrap their tokens into Eigen + */ + function wrap(uint256 amount) external; + + /** + * @notice This function allows Eigen holders to unwrap their tokens into bEIGEN + */ + function unwrap(uint256 amount) external; + + /** + * @dev Clock used for flagging checkpoints. Has been overridden to implement timestamp based + * checkpoints (and voting). + */ + function clock() external view returns (uint48); + + /** + * @dev Machine-readable description of the clock as specified in EIP-6372. + * Has been overridden to inform callers that this contract uses timestamps instead of block numbers, to match `clock()` + */ + // solhint-disable-next-line func-name-mixedcase + function CLOCK_MODE() external pure returns (string memory); +} diff --git a/mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenLayer.sol similarity index 99% rename from mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol rename to mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenLayer.sol index ff033e8c..c67d4030 100644 --- a/mainnet-contracts/src/interface/EigenLayer/IEigenLayer.sol +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenLayer.sol @@ -18,6 +18,7 @@ interface IEigenLayer { * the data is resubmitted and the hash of the submitted data is computed by `calculateWithdrawalRoot` and checked against the * stored hash in order to confirm the integrity of the submitted data. */ + struct QueuedWithdrawal { IStrategy[] strategies; uint256[] shares; diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol new file mode 100644 index 00000000..ec7f2e7e --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import "../libraries/BeaconChainProofs.sol"; +import "./IEigenPodManager.sol"; + +interface IEigenPodErrors { + /// @dev Thrown when msg.sender is not the EPM. + error OnlyEigenPodManager(); + /// @dev Thrown when msg.sender is not the pod owner. + error OnlyEigenPodOwner(); + /// @dev Thrown when msg.sender is not owner or the proof submitter. + error OnlyEigenPodOwnerOrProofSubmitter(); + /// @dev Thrown when attempting an action that is currently paused. + error CurrentlyPaused(); + + /// Invalid Inputs + + /// @dev Thrown when an address of zero is provided. + error InputAddressZero(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when `validatorPubKey` length is not equal to 48-bytes. + error InvalidPubKeyLength(); + /// @dev Thrown when provided timestamp is out of range. + error TimestampOutOfRange(); + + /// Checkpoints + + /// @dev Thrown when no active checkpoints are found. + error NoActiveCheckpoint(); + /// @dev Thrown if an uncompleted checkpoint exists. + error CheckpointAlreadyActive(); + /// @dev Thrown if there's not a balance available to checkpoint. + error NoBalanceToCheckpoint(); + /// @dev Thrown when attempting to create a checkpoint twice within a given block. + error CannotCheckpointTwiceInSingleBlock(); + + /// Withdrawing + + /// @dev Thrown when amount exceeds `restakedExecutionLayerGwei`. + error InsufficientWithdrawableBalance(); + /// @dev Thrown when provided `amountGwei` is not a multiple of gwei. + error AmountMustBeMultipleOfGwei(); + + /// Validator Status + + /// @dev Thrown when a validator's withdrawal credentials have already been verified. + error CredentialsAlreadyVerified(); + /// @dev Thrown if the provided proof is not valid for this EigenPod. + error WithdrawalCredentialsNotForEigenPod(); + /// @dev Thrown when a validator is not in the ACTIVE status in the pod. + error ValidatorNotActiveInPod(); + /// @dev Thrown when validator is not active yet on the beacon chain. + error ValidatorInactiveOnBeaconChain(); + /// @dev Thrown if a validator is exiting the beacon chain. + error ValidatorIsExitingBeaconChain(); + /// @dev Thrown when a validator has not been slashed on the beacon chain. + error ValidatorNotSlashedOnBeaconChain(); + + /// Misc + + /// @dev Thrown when an invalid block root is returned by the EIP-4788 oracle. + error InvalidEIP4788Response(); + /// @dev Thrown when attempting to send an invalid amount to the beacon deposit contract. + error MsgValueNot32ETH(); + /// @dev Thrown when provided `beaconTimestamp` is too far in the past. + error BeaconTimestampTooFarInPast(); +} + +interface IEigenPodTypes { + enum VALIDATOR_STATUS { + INACTIVE, // doesnt exist + ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod + WITHDRAWN // withdrawn from the Beacon Chain + + } + + struct ValidatorInfo { + // index of the validator in the beacon chain + uint64 validatorIndex; + // amount of beacon chain ETH restaked on EigenLayer in gwei + uint64 restakedBalanceGwei; + //timestamp of the validator's most recent balance update + uint64 lastCheckpointedAt; + // status of the validator + VALIDATOR_STATUS status; + } + + struct Checkpoint { + bytes32 beaconBlockRoot; + uint24 proofsRemaining; + uint64 podBalanceGwei; + int64 balanceDeltasGwei; + uint64 prevBeaconBalanceGwei; + } +} + +interface IEigenPodEvents is IEigenPodTypes { + /// @notice Emitted when an ETH validator stakes via this eigenPod + event EigenPodStaked(bytes pubkey); + + /// @notice Emitted when a pod owner updates the proof submitter address + event ProofSubmitterUpdated(address prevProofSubmitter, address newProofSubmitter); + + /// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod + event ValidatorRestaked(uint40 validatorIndex); + + /// @notice Emitted when an ETH validator's balance is proven to be updated. Here newValidatorBalanceGwei + // is the validator's balance that is credited on EigenLayer. + event ValidatorBalanceUpdated(uint40 validatorIndex, uint64 balanceTimestamp, uint64 newValidatorBalanceGwei); + + /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod. + event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount); + + /// @notice Emitted when ETH is received via the `receive` fallback + event NonBeaconChainETHReceived(uint256 amountReceived); + + /// @notice Emitted when a checkpoint is created + event CheckpointCreated( + uint64 indexed checkpointTimestamp, bytes32 indexed beaconBlockRoot, uint256 validatorCount + ); + + /// @notice Emitted when a checkpoint is finalized + event CheckpointFinalized(uint64 indexed checkpointTimestamp, int256 totalShareDeltaWei); + + /// @notice Emitted when a validator is proven for a given checkpoint + event ValidatorCheckpointed(uint64 indexed checkpointTimestamp, uint40 indexed validatorIndex); + + /// @notice Emitted when a validaor is proven to have 0 balance at a given checkpoint + event ValidatorWithdrawn(uint64 indexed checkpointTimestamp, uint40 indexed validatorIndex); +} + +/** + * @title The implementation contract used for restaking beacon chain ETH on EigenLayer + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @dev Note that all beacon chain balances are stored as gwei within the beacon chain datastructures. We choose + * to account balances in terms of gwei in the EigenPod contract and convert to wei when making calls to other contracts + */ +interface IEigenPod is IEigenPodErrors, IEigenPodEvents { + /// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager + function initialize(address owner) external; + + /// @notice Called by EigenPodManager when the owner wants to create another ETH validator. + function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable; + + /** + * @notice Transfers `amountWei` in ether from this contract to the specified `recipient` address + * @notice Called by EigenPodManager to withdrawBeaconChainETH that has been added to the EigenPod's balance due to a withdrawal from the beacon chain. + * @dev The podOwner must have already proved sufficient withdrawals, so that this pod's `restakedExecutionLayerGwei` exceeds the + * `amountWei` input (when converted to GWEI). + * @dev Reverts if `amountWei` is not a whole Gwei amount + */ + function withdrawRestakedBeaconChainETH(address recipient, uint256 amount) external; + + /** + * @dev Create a checkpoint used to prove this pod's active validator set. Checkpoints are completed + * by submitting one checkpoint proof per ACTIVE validator. During the checkpoint process, the total + * change in ACTIVE validator balance is tracked, and any validators with 0 balance are marked `WITHDRAWN`. + * @dev Once finalized, the pod owner is awarded shares corresponding to: + * - the total change in their ACTIVE validator balances + * - any ETH in the pod not already awarded shares + * @dev A checkpoint cannot be created if the pod already has an outstanding checkpoint. If + * this is the case, the pod owner MUST complete the existing checkpoint before starting a new one. + * @param revertIfNoBalance Forces a revert if the pod ETH balance is 0. This allows the pod owner + * to prevent accidentally starting a checkpoint that will not increase their shares + */ + function startCheckpoint(bool revertIfNoBalance) external; + + /** + * @dev Progress the current checkpoint towards completion by submitting one or more validator + * checkpoint proofs. Anyone can call this method to submit proofs towards the current checkpoint. + * For each validator proven, the current checkpoint's `proofsRemaining` decreases. + * @dev If the checkpoint's `proofsRemaining` reaches 0, the checkpoint is finalized. + * (see `_updateCheckpoint` for more details) + * @dev This method can only be called when there is a currently-active checkpoint. + * @param balanceContainerProof proves the beacon's current balance container root against a checkpoint's `beaconBlockRoot` + * @param proofs Proofs for one or more validator current balances against the `balanceContainerRoot` + */ + function verifyCheckpointProofs( + BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, + BeaconChainProofs.BalanceProof[] calldata proofs + ) external; + + /** + * @dev Verify one or more validators have their withdrawal credentials pointed at this EigenPod, and award + * shares based on their effective balance. Proven validators are marked `ACTIVE` within the EigenPod, and + * future checkpoint proofs will need to include them. + * @dev Withdrawal credential proofs MUST NOT be older than `currentCheckpointTimestamp`. + * @dev Validators proven via this method MUST NOT have an exit epoch set already. + * @param beaconTimestamp the beacon chain timestamp sent to the 4788 oracle contract. Corresponds + * to the parent beacon block root against which the proof is verified. + * @param stateRootProof proves a beacon state root against a beacon block root + * @param validatorIndices a list of validator indices being proven + * @param validatorFieldsProofs proofs of each validator's `validatorFields` against the beacon state root + * @param validatorFields the fields of the beacon chain "Validator" container. See consensus specs for + * details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator + */ + function verifyWithdrawalCredentials( + uint64 beaconTimestamp, + BeaconChainProofs.StateRootProof calldata stateRootProof, + uint40[] calldata validatorIndices, + bytes[] calldata validatorFieldsProofs, + bytes32[][] calldata validatorFields + ) external; + + /** + * @dev Prove that one of this pod's active validators was slashed on the beacon chain. A successful + * staleness proof allows the caller to start a checkpoint. + * + * @dev Note that in order to start a checkpoint, any existing checkpoint must already be completed! + * (See `_startCheckpoint` for details) + * + * @dev Note that this method allows anyone to start a checkpoint as soon as a slashing occurs on the beacon + * chain. This is intended to make it easier to external watchers to keep a pod's balance up to date. + * + * @dev Note too that beacon chain slashings are not instant. There is a delay between the initial slashing event + * and the validator's final exit back to the execution layer. During this time, the validator's balance may or + * may not drop further due to a correlation penalty. This method allows proof of a slashed validator + * to initiate a checkpoint for as long as the validator remains on the beacon chain. Once the validator + * has exited and been checkpointed at 0 balance, they are no longer "checkpoint-able" and cannot be proven + * "stale" via this method. + * See https://eth2book.info/capella/part3/transition/epoch/#slashings for more info. + * + * @param beaconTimestamp the beacon chain timestamp sent to the 4788 oracle contract. Corresponds + * to the parent beacon block root against which the proof is verified. + * @param stateRootProof proves a beacon state root against a beacon block root + * @param proof the fields of the beacon chain "Validator" container, along with a merkle proof against + * the beacon state root. See the consensus specs for more details: + * https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator + * + * @dev Staleness conditions: + * - Validator's last checkpoint is older than `beaconTimestamp` + * - Validator MUST be in `ACTIVE` status in the pod + * - Validator MUST be slashed on the beacon chain + */ + function verifyStaleBalance( + uint64 beaconTimestamp, + BeaconChainProofs.StateRootProof calldata stateRootProof, + BeaconChainProofs.ValidatorProof calldata proof + ) external; + + /// @notice called by owner of a pod to remove any ERC20s deposited in the pod + function recoverTokens(IERC20[] memory tokenList, uint256[] memory amountsToWithdraw, address recipient) external; + + /// @notice Allows the owner of a pod to update the proof submitter, a permissioned + /// address that can call `startCheckpoint` and `verifyWithdrawalCredentials`. + /// @dev Note that EITHER the podOwner OR proofSubmitter can access these methods, + /// so it's fine to set your proofSubmitter to 0 if you want the podOwner to be the + /// only address that can call these methods. + /// @param newProofSubmitter The new proof submitter address. If set to 0, only the + /// pod owner will be able to call `startCheckpoint` and `verifyWithdrawalCredentials` + function setProofSubmitter(address newProofSubmitter) external; + + /** + * + * VIEW METHODS + * + */ + + /// @notice An address with permissions to call `startCheckpoint` and `verifyWithdrawalCredentials`, set + /// by the podOwner. This role exists to allow a podOwner to designate a hot wallet that can call + /// these methods, allowing the podOwner to remain a cold wallet that is only used to manage funds. + /// @dev If this address is NOT set, only the podOwner can call `startCheckpoint` and `verifyWithdrawalCredentials` + function proofSubmitter() external view returns (address); + + /// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer), + function withdrawableRestakedExecutionLayerGwei() external view returns (uint64); + + /// @notice The single EigenPodManager for EigenLayer + function eigenPodManager() external view returns (IEigenPodManager); + + /// @notice The owner of this EigenPod + function podOwner() external view returns (address); + + /// @notice Returns the validatorInfo struct for the provided pubkeyHash + function validatorPubkeyHashToInfo(bytes32 validatorPubkeyHash) external view returns (ValidatorInfo memory); + + /// @notice Returns the validatorInfo struct for the provided pubkey + function validatorPubkeyToInfo(bytes calldata validatorPubkey) external view returns (ValidatorInfo memory); + + /// @notice This returns the status of a given validator + function validatorStatus(bytes32 pubkeyHash) external view returns (VALIDATOR_STATUS); + + /// @notice This returns the status of a given validator pubkey + function validatorStatus(bytes calldata validatorPubkey) external view returns (VALIDATOR_STATUS); + + /// @notice Number of validators with proven withdrawal credentials, who do not have proven full withdrawals + function activeValidatorCount() external view returns (uint256); + + /// @notice The timestamp of the last checkpoint finalized + function lastCheckpointTimestamp() external view returns (uint64); + + /// @notice The timestamp of the currently-active checkpoint. Will be 0 if there is not active checkpoint + function currentCheckpointTimestamp() external view returns (uint64); + + /// @notice Returns the currently-active checkpoint + function currentCheckpoint() external view returns (Checkpoint memory); + + /// @notice For each checkpoint, the total balance attributed to exited validators, in gwei + /// + /// NOTE that the values added to this mapping are NOT guaranteed to capture the entirety of a validator's + /// exit - rather, they capture the total change in a validator's balance when a checkpoint shows their + /// balance change from nonzero to zero. While a change from nonzero to zero DOES guarantee that a validator + /// has been fully exited, it is possible that the magnitude of this change does not capture what is + /// typically thought of as a "full exit." + /// + /// For example: + /// 1. Consider a validator was last checkpointed at 32 ETH before exiting. Once the exit has been processed, + /// it is expected that the validator's exited balance is calculated to be `32 ETH`. + /// 2. However, before `startCheckpoint` is called, a deposit is made to the validator for 1 ETH. The beacon + /// chain will automatically withdraw this ETH, but not until the withdrawal sweep passes over the validator + /// again. Until this occurs, the validator's current balance (used for checkpointing) is 1 ETH. + /// 3. If `startCheckpoint` is called at this point, the balance delta calculated for this validator will be + /// `-31 ETH`, and because the validator has a nonzero balance, it is not marked WITHDRAWN. + /// 4. After the exit is processed by the beacon chain, a subsequent `startCheckpoint` and checkpoint proof + /// will calculate a balance delta of `-1 ETH` and attribute a 1 ETH exit to the validator. + /// + /// If this edge case impacts your usecase, it should be possible to mitigate this by monitoring for deposits + /// to your exited validators, and waiting to call `startCheckpoint` until those deposits have been automatically + /// exited. + /// + /// Additional edge cases this mapping does not cover: + /// - If a validator is slashed, their balance exited will reflect their original balance rather than the slashed amount + /// - The final partial withdrawal for an exited validator will be likely be included in this mapping. + /// i.e. if a validator was last checkpointed at 32.1 ETH before exiting, the next checkpoint will calculate their + /// "exited" amount to be 32.1 ETH rather than 32 ETH. + function checkpointBalanceExitedGwei(uint64) external view returns (uint64); + + /// @notice Query the 4788 oracle to get the parent block root of the slot with the given `timestamp` + /// @param timestamp of the block for which the parent block root will be returned. MUST correspond + /// to an existing slot within the last 24 hours. If the slot at `timestamp` was skipped, this method + /// will revert. + function getParentBlockRoot(uint64 timestamp) external view returns (bytes32); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPodManager.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPodManager.sol new file mode 100644 index 00000000..9e4e4428 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPodManager.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +import "./IETHPOSDeposit.sol"; +import "./IStrategyManager.sol"; +import "./IEigenPod.sol"; +import "./IShareManager.sol"; +import "./IPausable.sol"; +import "./IStrategy.sol"; + +interface IEigenPodManagerErrors { + /// @dev Thrown when caller is not a EigenPod. + error OnlyEigenPod(); + /// @dev Thrown when caller is not DelegationManager. + error OnlyDelegationManager(); + /// @dev Thrown when caller already has an EigenPod. + error EigenPodAlreadyExists(); + /// @dev Thrown when shares is not a multiple of gwei. + error SharesNotMultipleOfGwei(); + /// @dev Thrown when shares would result in a negative integer. + error SharesNegative(); + /// @dev Thrown when the strategy is not the beaconChainETH strategy. + error InvalidStrategy(); + /// @dev Thrown when the pods shares are negative and a beacon chain balance update is attempted. + /// The podOwner should complete legacy withdrawal first. + error LegacyWithdrawalsNotCompleted(); +} + +interface IEigenPodManagerEvents { + /// @notice Emitted to notify the deployment of an EigenPod + event PodDeployed(address indexed eigenPod, address indexed podOwner); + + /// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager + event BeaconChainETHDeposited(address indexed podOwner, uint256 amount); + + /// @notice Emitted when the balance of an EigenPod is updated + event PodSharesUpdated(address indexed podOwner, int256 sharesDelta); + + /// @notice Emitted every time the total shares of a pod are updated + event NewTotalShares(address indexed podOwner, int256 newTotalShares); + + /// @notice Emitted when a withdrawal of beacon chain ETH is completed + event BeaconChainETHWithdrawalCompleted( + address indexed podOwner, + uint256 shares, + uint96 nonce, + address delegatedAddress, + address withdrawer, + bytes32 withdrawalRoot + ); + + /// @notice Emitted when a staker's beaconChainSlashingFactor is updated + event BeaconChainSlashingFactorDecreased( + address staker, uint64 prevBeaconChainSlashingFactor, uint64 newBeaconChainSlashingFactor + ); +} + +interface IEigenPodManagerTypes { + /** + * @notice The amount of beacon chain slashing experienced by a pod owner as a proportion of WAD + * @param isSet whether the slashingFactor has ever been updated. Used to distinguish between + * a value of "0" and an uninitialized value. + * @param slashingFactor the proportion of the pod owner's balance that has been decreased due to + * slashing or other beacon chain balance decreases. + * @dev NOTE: if !isSet, `slashingFactor` should be treated as WAD. `slashingFactor` is monotonically + * decreasing and can hit 0 if fully slashed. + */ + struct BeaconChainSlashingFactor { + bool isSet; + uint64 slashingFactor; + } +} + +/** + * @title Interface for factory that creates and manages solo staking pods that have their withdrawal credentials pointed to EigenLayer. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + */ +interface IEigenPodManager is + IEigenPodManagerErrors, + IEigenPodManagerEvents, + IEigenPodManagerTypes, + IShareManager, + IPausable +{ + /** + * @notice Creates an EigenPod for the sender. + * @dev Function will revert if the `msg.sender` already has an EigenPod. + * @dev Returns EigenPod address + */ + function createPod() external returns (address); + + /** + * @notice Stakes for a new beacon chain validator on the sender's EigenPod. + * Also creates an EigenPod for the sender if they don't have one already. + * @param pubkey The 48 bytes public key of the beacon chain validator. + * @param signature The validator's signature of the deposit data. + * @param depositDataRoot The root/hash of the deposit data for the validator's deposit. + */ + function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable; + + /** + * @notice Changes the `podOwner`'s shares by `sharesDelta` and performs a call to the DelegationManager + * to ensure that delegated shares are also tracked correctly + * @param podOwner is the pod owner whose balance is being updated. + * @param prevRestakedBalanceWei is the total amount restaked through the pod before the balance update + * @param balanceDeltaWei is the amount the balance changed + * @dev Callable only by the podOwner's EigenPod contract. + * @dev Reverts if `sharesDelta` is not a whole Gwei amount + */ + function recordBeaconChainETHBalanceUpdate(address podOwner, uint256 prevRestakedBalanceWei, int256 balanceDeltaWei) + external; + + /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed. + function ownerToPod(address podOwner) external view returns (IEigenPod); + + /// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not). + function getPod(address podOwner) external view returns (IEigenPod); + + /// @notice The ETH2 Deposit Contract + function ethPOS() external view returns (IETHPOSDeposit); + + /// @notice Beacon proxy to which the EigenPods point + function eigenPodBeacon() external view returns (IBeacon); + + /// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise. + function hasPod(address podOwner) external view returns (bool); + + /// @notice Returns the number of EigenPods that have been created + function numPods() external view returns (uint256); + + /** + * @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy. + * @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can + * decrease between the pod owner queuing and completing a withdrawal. + * When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_. + * Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this + * as the withdrawal "paying off the deficit". + */ + function podOwnerDepositShares(address podOwner) external view returns (int256); + + /// @notice returns canonical, virtual beaconChainETH strategy + function beaconChainETHStrategy() external view returns (IStrategy); + + /** + * @notice Returns the historical sum of proportional balance decreases a pod owner has experienced when + * updating their pod's balance. + */ + function beaconChainSlashingFactor(address staker) external view returns (uint64); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPausable.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPausable.sol new file mode 100644 index 00000000..f139836b --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPausable.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "./IPauserRegistry.sol"; + +/** + * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions. + * These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control. + * @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality. + * Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code. + * For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause, + * you can only flip (any number of) switches to off/0 (aka "paused"). + * If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will: + * 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256) + * 2) update the paused state to this new value + * @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3` + * indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused + */ +interface IPausable { + /// @dev Thrown when caller is not pauser. + error OnlyPauser(); + /// @dev Thrown when caller is not unpauser. + error OnlyUnpauser(); + /// @dev Thrown when currently paused. + error CurrentlyPaused(); + /// @dev Thrown when invalid `newPausedStatus` is provided. + error InvalidNewPausedStatus(); + /// @dev Thrown when a null address input is provided. + error InputAddressZero(); + + /// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`. + event Paused(address indexed account, uint256 newPausedStatus); + + /// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`. + event Unpaused(address indexed account, uint256 newPausedStatus); + + /** + * @notice This function is used to pause an EigenLayer contract's functionality. + * It is permissioned to the `pauser` address, which is expected to be a low threshold multisig. + * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once. + * @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0. + */ + function pause(uint256 newPausedStatus) external; + + /** + * @notice Alias for `pause(type(uint256).max)`. + */ + function pauseAll() external; + + /** + * @notice This function is used to unpause an EigenLayer contract's functionality. + * It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract. + * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once. + * @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1. + */ + function unpause(uint256 newPausedStatus) external; + + /// @notice Returns the current paused status as a uint256. + function paused() external view returns (uint256); + + /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise + function paused(uint8 index) external view returns (bool); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPauserRegistry.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPauserRegistry.sol new file mode 100644 index 00000000..9a058bcd --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPauserRegistry.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +/** + * @title Interface for the `PauserRegistry` contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + */ +interface IPauserRegistry { + error OnlyUnpauser(); + error InputAddressZero(); + + event PauserStatusChanged(address pauser, bool canPause); + + event UnpauserChanged(address previousUnpauser, address newUnpauser); + + /// @notice Mapping of addresses to whether they hold the pauser role. + function isPauser(address pauser) external view returns (bool); + + /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses. + function unpauser() external view returns (address); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol new file mode 100644 index 00000000..00337742 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +interface IPermissionControllerErrors { + /// @notice Thrown when the caller is not the admin + error NotAdmin(); + /// @notice Thrown when the admin to remove is not an admin + error AdminNotSet(); + /// @notice Thrown when an appointee is already set for the account's function + error AppointeeAlreadySet(); + /// @notice Thrown when an appointee is not set for the account's function + error AppointeeNotSet(); + /// @notice Thrown when the account attempts to remove the only admin + error CannotHaveZeroAdmins(); + /// @notice Thrown when an admin is already set + error AdminAlreadySet(); + /// @notice Thrown when an admin is not pending + error AdminNotPending(); + /// @notice Thrown when an admin is already pending + error AdminAlreadyPending(); +} + +interface IPermissionControllerEvents { + /// @notice Emitted when an appointee is set + event AppointeeSet(address indexed account, address indexed appointee, address target, bytes4 selector); + + /// @notice Emitted when an appointee is revoked + event AppointeeRemoved(address indexed account, address indexed appointee, address target, bytes4 selector); + + /// @notice Emitted when an admin is set as pending for an account + event PendingAdminAdded(address indexed account, address admin); + + /// @notice Emitted when an admin is removed as pending for an account + event PendingAdminRemoved(address indexed account, address admin); + + /// @notice Emitted when an admin is set for a given account + event AdminSet(address indexed account, address admin); + + /// @notice Emitted when an admin is removed for a given account + event AdminRemoved(address indexed account, address admin); +} + +interface IPermissionController is IPermissionControllerErrors, IPermissionControllerEvents { + /** + * @notice Sets a pending admin of an account + * @param account to set pending admin for + * @param admin to set + * @dev Multiple admins can be set for an account + */ + function addPendingAdmin(address account, address admin) external; + + /** + * @notice Removes a pending admin of an account + * @param account to remove pending admin for + * @param admin to remove + * @dev Only the admin of the account can remove a pending admin + */ + function removePendingAdmin(address account, address admin) external; + + /** + * @notice Accepts the admin role of an account + * @param account to accept admin for + * @dev Only a pending admin for the account can become an admin + */ + function acceptAdmin(address account) external; + + /** + * @notice Remove an admin of an account + * @param account to remove admin for + * @param admin to remove + * @dev Only the admin of the account can remove an admin + * @dev Reverts when an admin is removed such that no admins are remaining + */ + function removeAdmin(address account, address admin) external; + + /** + * @notice Set an appointee for a given account + * @param account to set appointee for + * @param appointee to set + * @param target to set appointee for + * @param selector to set appointee for + * @dev Only the admin of the account can set an appointee + */ + function setAppointee(address account, address appointee, address target, bytes4 selector) external; + + /** + * Removes an appointee for a given account + * @param account to remove appointee for + * @param appointee to remove + * @param target to remove appointee for + * @param selector to remove appointee for + * @dev Only the admin of the account can remove an appointee + */ + function removeAppointee(address account, address appointee, address target, bytes4 selector) external; + + /** + * @notice Checks if the given caller is an admin of the account + * @dev If the account has no admin, the caller is checked to be the account itself + */ + function isAdmin(address account, address caller) external view returns (bool); + + /** + * @notice Checks if the `pendingAdmin` is a pending admin of the `account` + */ + function isPendingAdmin(address account, address pendingAdmin) external view returns (bool); + + /** + * @notice Get the admins of an account + * @param account The account to get the admin of + * @dev If the account has no admin, the account itself is returned + */ + function getAdmins(address account) external view returns (address[] memory); + + /** + * @notice Get the pending admins of an account + * @param account The account to get the pending admin of + */ + function getPendingAdmins(address account) external view returns (address[] memory); + + /** + * @notice Checks if the given caller has permissions to call the fucntion + * @param account to check + * @param caller to check permission for + * @param target to check permission for + * @param selector to check permission for + * @dev Returns `true` if the admin OR the appointee is the caller + */ + function canCall(address account, address caller, address target, bytes4 selector) external returns (bool); + + /** + * @notice Gets the list of permissions of an appointee for a given account + * @param account to get appointee permissions for + * @param appointee to get permissions + */ + function getAppointeePermissions(address account, address appointee) + external + returns (address[] memory, bytes4[] memory); + + /** + * @notice Returns the list of appointees for a given account and function + * @param account to get appointees for + * @param target to get appointees for + * @param selector to get appointees for + * @dev Does NOT include admin as an appointee, even though it can call + */ + function getAppointees(address account, address target, bytes4 selector) external returns (address[] memory); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol new file mode 100644 index 00000000..c858dceb --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "./IPauserRegistry.sol"; +import "./IStrategy.sol"; + +interface IRewardsCoordinatorErrors { + /// @dev Thrown when msg.sender is not allowed to call a function + error UnauthorizedCaller(); + /// @dev Thrown when a earner not an AVS or Operator + error InvalidEarner(); + + /// Invalid Inputs + + /// @dev Thrown when an input address is zero + error InvalidAddressZero(); + /// @dev Thrown when an invalid root is provided. + error InvalidRoot(); + /// @dev Thrown when an invalid root index is provided. + error InvalidRootIndex(); + /// @dev Thrown when input arrays length is zero. + error InputArrayLengthZero(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when provided root is not for new calculated period. + error NewRootMustBeForNewCalculatedPeriod(); + /// @dev Thrown when rewards end timestamp has not elapsed. + error RewardsEndTimestampNotElapsed(); + + /// Rewards Submissions + + /// @dev Thrown when input `amount` is zero. + error AmountIsZero(); + /// @dev Thrown when input `amount` exceeds maximum. + error AmountExceedsMax(); + /// @dev Thrown when input `split` exceeds `ONE_HUNDRED_IN_BIPS` + error SplitExceedsMax(); + /// @dev Thrown when input `duration` exceeds maximum. + error DurationExceedsMax(); + /// @dev Thrown when input `duration` is not evenly divisble by CALCULATION_INTERVAL_SECONDS. + error InvalidDurationRemainder(); + /// @dev Thrown when GENESIS_REWARDS_TIMESTAMP is not evenly divisble by CALCULATION_INTERVAL_SECONDS. + error InvalidGenesisRewardsTimestampRemainder(); + /// @dev Thrown when CALCULATION_INTERVAL_SECONDS is not evenly divisble by SNAPSHOT_CADENCE. + error InvalidCalculationIntervalSecondsRemainder(); + /// @dev Thrown when `startTimestamp` is not evenly divisble by CALCULATION_INTERVAL_SECONDS. + error InvalidStartTimestampRemainder(); + /// @dev Thrown when `startTimestamp` is too far in the future. + error StartTimestampTooFarInFuture(); + /// @dev Thrown when `startTimestamp` is too far in the past. + error StartTimestampTooFarInPast(); + /// @dev Thrown when an attempt to use a non-whitelisted strategy is made. + error StrategyNotWhitelisted(); + /// @dev Thrown when `strategies` is not sorted in ascending order. + error StrategiesNotInAscendingOrder(); + /// @dev Thrown when `operators` are not sorted in ascending order + error OperatorsNotInAscendingOrder(); + /// @dev Thrown when an operator-directed rewards submission is not retroactive + error SubmissionNotRetroactive(); + + /// Claims + + /// @dev Thrown when an invalid earner claim proof is provided. + error InvalidClaimProof(); + /// @dev Thrown when an invalid token leaf index is provided. + error InvalidTokenLeafIndex(); + /// @dev Thrown when an invalid earner leaf index is provided. + error InvalidEarnerLeafIndex(); + /// @dev Thrown when cummulative earnings are not greater than cummulative claimed. + error EarningsNotGreaterThanClaimed(); + + /// Reward Root Checks + + /// @dev Thrown if a root has already been disabled. + error RootDisabled(); + /// @dev Thrown if a root has not been activated yet. + error RootNotActivated(); + /// @dev Thrown if a root has already been activated. + error RootAlreadyActivated(); +} + +interface IRewardsCoordinatorTypes { + /** + * @notice A linear combination of strategies and multipliers for AVSs to weigh + * EigenLayer strategies. + * @param strategy The EigenLayer strategy to be used for the rewards submission + * @param multiplier The weight of the strategy in the rewards submission + */ + struct StrategyAndMultiplier { + IStrategy strategy; + uint96 multiplier; + } + + /** + * @notice A reward struct for an operator + * @param operator The operator to be rewarded + * @param amount The reward amount for the operator + */ + struct OperatorReward { + address operator; + uint256 amount; + } + + /** + * @notice A split struct for an Operator + * @param oldSplitBips The old split in basis points. This is the split that is active if `block.timestamp < activatedAt` + * @param newSplitBips The new split in basis points. This is the split that is active if `block.timestamp >= activatedAt` + * @param activatedAt The timestamp at which the split will be activated + */ + struct OperatorSplit { + uint16 oldSplitBips; + uint16 newSplitBips; + uint32 activatedAt; + } + + /** + * Sliding Window for valid RewardsSubmission startTimestamp + * + * Scenario A: GENESIS_REWARDS_TIMESTAMP IS WITHIN RANGE + * <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH---> + * <--------------------valid range for startTimestamp------------------------> + * ^ + * GENESIS_REWARDS_TIMESTAMP + * + * + * Scenario B: GENESIS_REWARDS_TIMESTAMP IS OUT OF RANGE + * <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH---> + * <------------------------valid range for startTimestamp------------------------> + * ^ + * GENESIS_REWARDS_TIMESTAMP + * @notice RewardsSubmission struct submitted by AVSs when making rewards for their operators and stakers + * RewardsSubmission can be for a time range within the valid window for startTimestamp and must be within max duration. + * See `createAVSRewardsSubmission()` for more details. + * @param strategiesAndMultipliers The strategies and their relative weights + * cannot have duplicate strategies and need to be sorted in ascending address order + * @param token The rewards token to be distributed + * @param amount The total amount of tokens to be distributed + * @param startTimestamp The timestamp (seconds) at which the submission range is considered for distribution + * could start in the past or in the future but within a valid range. See the diagram above. + * @param duration The duration of the submission range in seconds. Must be <= MAX_REWARDS_DURATION + */ + struct RewardsSubmission { + StrategyAndMultiplier[] strategiesAndMultipliers; + IERC20 token; + uint256 amount; + uint32 startTimestamp; + uint32 duration; + } + + /** + * @notice OperatorDirectedRewardsSubmission struct submitted by AVSs when making operator-directed rewards for their operators and stakers. + * @param strategiesAndMultipliers The strategies and their relative weights. + * @param token The rewards token to be distributed. + * @param operatorRewards The rewards for the operators. + * @param startTimestamp The timestamp (seconds) at which the submission range is considered for distribution. + * @param duration The duration of the submission range in seconds. + * @param description Describes what the rewards submission is for. + */ + struct OperatorDirectedRewardsSubmission { + StrategyAndMultiplier[] strategiesAndMultipliers; + IERC20 token; + OperatorReward[] operatorRewards; + uint32 startTimestamp; + uint32 duration; + string description; + } + + /** + * @notice A distribution root is a merkle root of the distribution of earnings for a given period. + * The RewardsCoordinator stores all historical distribution roots so that earners can claim their earnings against older roots + * if they wish but the merkle tree contains the cumulative earnings of all earners and tokens for a given period so earners (or their claimers if set) + * only need to claim against the latest root to claim all available earnings. + * @param root The merkle root of the distribution + * @param rewardsCalculationEndTimestamp The timestamp (seconds) until which rewards have been calculated + * @param activatedAt The timestamp (seconds) at which the root can be claimed against + */ + struct DistributionRoot { + bytes32 root; + uint32 rewardsCalculationEndTimestamp; + uint32 activatedAt; + bool disabled; + } + + /** + * @notice Internal leaf in the merkle tree for the earner's account leaf + * @param earner The address of the earner + * @param earnerTokenRoot The merkle root of the earner's token subtree + * Each leaf in the earner's token subtree is a TokenTreeMerkleLeaf + */ + struct EarnerTreeMerkleLeaf { + address earner; + bytes32 earnerTokenRoot; + } + + /** + * @notice The actual leaves in the distribution merkle tree specifying the token earnings + * for the respective earner's subtree. Each leaf is a claimable amount of a token for an earner. + * @param token The token for which the earnings are being claimed + * @param cumulativeEarnings The cumulative earnings of the earner for the token + */ + struct TokenTreeMerkleLeaf { + IERC20 token; + uint256 cumulativeEarnings; + } + + /** + * @notice A claim against a distribution root called by an + * earners claimer (could be the earner themselves). Each token claim will claim the difference + * between the cumulativeEarnings of the earner and the cumulativeClaimed of the claimer. + * Each claim can specify which of the earner's earned tokens they want to claim. + * See `processClaim()` for more details. + * @param rootIndex The index of the root in the list of DistributionRoots + * @param earnerIndex The index of the earner's account root in the merkle tree + * @param earnerTreeProof The proof of the earner's EarnerTreeMerkleLeaf against the merkle root + * @param earnerLeaf The earner's EarnerTreeMerkleLeaf struct, providing the earner address and earnerTokenRoot + * @param tokenIndices The indices of the token leaves in the earner's subtree + * @param tokenTreeProofs The proofs of the token leaves against the earner's earnerTokenRoot + * @param tokenLeaves The token leaves to be claimed + * @dev The merkle tree is structured with the merkle root at the top and EarnerTreeMerkleLeaf as internal leaves + * in the tree. Each earner leaf has its own subtree with TokenTreeMerkleLeaf as leaves in the subtree. + * To prove a claim against a specified rootIndex(which specifies the distributionRoot being used), + * the claim will first verify inclusion of the earner leaf in the tree against _distributionRoots[rootIndex].root. + * Then for each token, it will verify inclusion of the token leaf in the earner's subtree against the earner's earnerTokenRoot. + */ + struct RewardsMerkleClaim { + uint32 rootIndex; + uint32 earnerIndex; + bytes earnerTreeProof; + EarnerTreeMerkleLeaf earnerLeaf; + uint32[] tokenIndices; + bytes[] tokenTreeProofs; + TokenTreeMerkleLeaf[] tokenLeaves; + } +} + +interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes { + /// @notice emitted when an AVS creates a valid RewardsSubmission + event AVSRewardsSubmissionCreated( + address indexed avs, + uint256 indexed submissionNonce, + bytes32 indexed rewardsSubmissionHash, + RewardsSubmission rewardsSubmission + ); + + /// @notice emitted when a valid RewardsSubmission is created for all stakers by a valid submitter + event RewardsSubmissionForAllCreated( + address indexed submitter, + uint256 indexed submissionNonce, + bytes32 indexed rewardsSubmissionHash, + RewardsSubmission rewardsSubmission + ); + + /// @notice emitted when a valid RewardsSubmission is created when rewardAllStakersAndOperators is called + event RewardsSubmissionForAllEarnersCreated( + address indexed tokenHopper, + uint256 indexed submissionNonce, + bytes32 indexed rewardsSubmissionHash, + RewardsSubmission rewardsSubmission + ); + + /** + * @notice Emitted when an AVS creates a valid `OperatorDirectedRewardsSubmission` + * @param caller The address calling `createOperatorDirectedAVSRewardsSubmission`. + * @param avs The avs on behalf of which the operator-directed rewards are being submitted. + * @param operatorDirectedRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `operatorDirectedRewardsSubmission`). + * @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash. + * @param operatorDirectedRewardsSubmission The Operator-Directed Rewards Submission. Contains the token, start timestamp, duration, operator rewards, description and, strategy and multipliers. + */ + event OperatorDirectedAVSRewardsSubmissionCreated( + address indexed caller, + address indexed avs, + bytes32 indexed operatorDirectedRewardsSubmissionHash, + uint256 submissionNonce, + OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission + ); + + /// @notice rewardsUpdater is responsible for submiting DistributionRoots, only owner can set rewardsUpdater + event RewardsUpdaterSet(address indexed oldRewardsUpdater, address indexed newRewardsUpdater); + + event RewardsForAllSubmitterSet( + address indexed rewardsForAllSubmitter, bool indexed oldValue, bool indexed newValue + ); + + event ActivationDelaySet(uint32 oldActivationDelay, uint32 newActivationDelay); + event DefaultOperatorSplitBipsSet(uint16 oldDefaultOperatorSplitBips, uint16 newDefaultOperatorSplitBips); + + /** + * @notice Emitted when the operator split for an AVS is set. + * @param caller The address calling `setOperatorAVSSplit`. + * @param operator The operator on behalf of which the split is being set. + * @param avs The avs for which the split is being set by the operator. + * @param activatedAt The timestamp at which the split will be activated. + * @param oldOperatorAVSSplitBips The old split for the operator for the AVS. + * @param newOperatorAVSSplitBips The new split for the operator for the AVS. + */ + event OperatorAVSSplitBipsSet( + address indexed caller, + address indexed operator, + address indexed avs, + uint32 activatedAt, + uint16 oldOperatorAVSSplitBips, + uint16 newOperatorAVSSplitBips + ); + + /** + * @notice Emitted when the operator split for Programmatic Incentives is set. + * @param caller The address calling `setOperatorPISplit`. + * @param operator The operator on behalf of which the split is being set. + * @param activatedAt The timestamp at which the split will be activated. + * @param oldOperatorPISplitBips The old split for the operator for Programmatic Incentives. + * @param newOperatorPISplitBips The new split for the operator for Programmatic Incentives. + */ + event OperatorPISplitBipsSet( + address indexed caller, + address indexed operator, + uint32 activatedAt, + uint16 oldOperatorPISplitBips, + uint16 newOperatorPISplitBips + ); + + event ClaimerForSet(address indexed earner, address indexed oldClaimer, address indexed claimer); + + /// @notice rootIndex is the specific array index of the newly created root in the storage array + event DistributionRootSubmitted( + uint32 indexed rootIndex, + bytes32 indexed root, + uint32 indexed rewardsCalculationEndTimestamp, + uint32 activatedAt + ); + + event DistributionRootDisabled(uint32 indexed rootIndex); + + /// @notice root is one of the submitted distribution roots that was claimed against + event RewardsClaimed( + bytes32 root, + address indexed earner, + address indexed claimer, + address indexed recipient, + IERC20 token, + uint256 claimedAmount + ); +} + +/** + * @title Interface for the `IRewardsCoordinator` contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice Allows AVSs to make "Rewards Submissions", which get distributed amongst the AVSs' confirmed + * Operators and the Stakers delegated to those Operators. + * Calculations are performed based on the completed RewardsSubmission, with the results posted in + * a Merkle root against which Stakers & Operators can make claims. + */ +interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorEvents { + /** + * @dev Initializes the addresses of the initial owner, pauser registry, rewardsUpdater and + * configures the initial paused status, activationDelay, and defaultOperatorSplitBips. + */ + function initialize( + address initialOwner, + uint256 initialPausedStatus, + address _rewardsUpdater, + uint32 _activationDelay, + uint16 _defaultSplitBips + ) external; + + /** + * @notice Creates a new rewards submission on behalf of an AVS, to be split amongst the + * set of stakers delegated to operators who are registered to the `avs` + * @param rewardsSubmissions The rewards submissions being created + * @dev Expected to be called by the ServiceManager of the AVS on behalf of which the submission is being made + * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` + * @dev The tokens are sent to the `RewardsCoordinator` contract + * @dev Strategies must be in ascending order of addresses to check for duplicates + * @dev This function will revert if the `rewardsSubmission` is malformed, + * e.g. if the `strategies` and `weights` arrays are of non-equal lengths + */ + function createAVSRewardsSubmission(RewardsSubmission[] calldata rewardsSubmissions) external; + + /** + * @notice similar to `createAVSRewardsSubmission` except the rewards are split amongst *all* stakers + * rather than just those delegated to operators who are registered to a single avs and is + * a permissioned call based on isRewardsForAllSubmitter mapping. + * @param rewardsSubmissions The rewards submissions being created + */ + function createRewardsForAllSubmission(RewardsSubmission[] calldata rewardsSubmissions) external; + + /** + * @notice Creates a new rewards submission for all earners across all AVSs. + * Earners in this case indicating all operators and their delegated stakers. Undelegated stake + * is not rewarded from this RewardsSubmission. This interface is only callable + * by the token hopper contract from the Eigen Foundation + * @param rewardsSubmissions The rewards submissions being created + */ + function createRewardsForAllEarners(RewardsSubmission[] calldata rewardsSubmissions) external; + + /** + * @notice Creates a new operator-directed rewards submission on behalf of an AVS, to be split amongst the operators and + * set of stakers delegated to operators who are registered to the `avs`. + * @param avs The AVS on behalf of which the reward is being submitted + * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created + * @dev Expected to be called by the ServiceManager of the AVS on behalf of which the submission is being made + * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` + * @dev The tokens are sent to the `RewardsCoordinator` contract + * @dev The `RewardsCoordinator` contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function. + * @dev Strategies must be in ascending order of addresses to check for duplicates + * @dev Operators must be in ascending order of addresses to check for duplicates. + * @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed. + */ + function createOperatorDirectedAVSRewardsSubmission( + address avs, + OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions + ) external; + + /** + * @notice Claim rewards against a given root (read from _distributionRoots[claim.rootIndex]). + * Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for, + * they can simply claim against the latest root and the contract will calculate the difference between + * their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address. + * @param claim The RewardsMerkleClaim to be processed. + * Contains the root index, earner, token leaves, and required proofs + * @param recipient The address recipient that receives the ERC20 rewards + * @dev only callable by the valid claimer, that is + * if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only + * claimerFor[claim.earner] can claim the rewards. + */ + function processClaim(RewardsMerkleClaim calldata claim, address recipient) external; + + /** + * @notice Batch claim rewards against a given root (read from _distributionRoots[claim.rootIndex]). + * Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for, + * they can simply claim against the latest root and the contract will calculate the difference between + * their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address. + * @param claims The RewardsMerkleClaims to be processed. + * Contains the root index, earner, token leaves, and required proofs + * @param recipient The address recipient that receives the ERC20 rewards + * @dev only callable by the valid claimer, that is + * if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only + * claimerFor[claim.earner] can claim the rewards. + * @dev This function may fail to execute with a large number of claims due to gas limits. Use a smaller array of claims if necessary. + */ + function processClaims(RewardsMerkleClaim[] calldata claims, address recipient) external; + + /** + * @notice Creates a new distribution root. activatedAt is set to block.timestamp + activationDelay + * @param root The merkle root of the distribution + * @param rewardsCalculationEndTimestamp The timestamp until which rewards have been calculated + * @dev Only callable by the rewardsUpdater + */ + function submitRoot(bytes32 root, uint32 rewardsCalculationEndTimestamp) external; + + /** + * @notice allow the rewardsUpdater to disable/cancel a pending root submission in case of an error + * @param rootIndex The index of the root to be disabled + */ + function disableRoot(uint32 rootIndex) external; + + /** + * @notice Sets the address of the entity that can call `processClaim` on ehalf of an earner + * @param claimer The address of the entity that can call `processClaim` on behalf of the earner + * @dev Assumes msg.sender is the earner + */ + function setClaimerFor(address claimer) external; + + /** + * @notice Sets the address of the entity that can call `processClaim` on behalf of an earner + * @param earner The address to set the claimer for + * @param claimer The address of the entity that can call `processClaim` on behalf of the earner + * @dev Only callable by operators or AVSs. We define an AVS that has created at least one + * operatorSet in the `AllocationManager` + */ + function setClaimerFor(address earner, address claimer) external; + + /** + * @notice Sets the delay in timestamp before a posted root can be claimed against + * @dev Only callable by the contract owner + * @param _activationDelay The new value for activationDelay + */ + function setActivationDelay(uint32 _activationDelay) external; + + /** + * @notice Sets the default split for all operators across all avss. + * @param split The default split for all operators across all avss in bips. + * @dev Only callable by the contract owner. + */ + function setDefaultOperatorSplit(uint16 split) external; + + /** + * @notice Sets the split for a specific operator for a specific avs + * @param operator The operator who is setting the split + * @param avs The avs for which the split is being set by the operator + * @param split The split for the operator for the specific avs in bips. + * @dev Only callable by the operator + * @dev Split has to be between 0 and 10000 bips (inclusive) + * @dev The split will be activated after the activation delay + */ + function setOperatorAVSSplit(address operator, address avs, uint16 split) external; + + /** + * @notice Sets the split for a specific operator for Programmatic Incentives. + * @param operator The operator on behalf of which the split is being set. + * @param split The split for the operator for Programmatic Incentives in bips. + * @dev Only callable by the operator + * @dev Split has to be between 0 and 10000 bips (inclusive) + * @dev The split will be activated after the activation delay + */ + function setOperatorPISplit(address operator, uint16 split) external; + + /** + * @notice Sets the permissioned `rewardsUpdater` address which can post new roots + * @dev Only callable by the contract owner + * @param _rewardsUpdater The address of the new rewardsUpdater + */ + function setRewardsUpdater(address _rewardsUpdater) external; + + /** + * @notice Sets the permissioned `rewardsForAllSubmitter` address which can submit createRewardsForAllSubmission + * @dev Only callable by the contract owner + * @param _submitter The address of the rewardsForAllSubmitter + * @param _newValue The new value for isRewardsForAllSubmitter + */ + function setRewardsForAllSubmitter(address _submitter, bool _newValue) external; + + /** + * + * VIEW FUNCTIONS + * + */ + + /// @notice Delay in timestamp (seconds) before a posted root can be claimed against + function activationDelay() external view returns (uint32); + + /// @notice The timestamp until which RewardsSubmissions have been calculated + function currRewardsCalculationEndTimestamp() external view returns (uint32); + + /// @notice Mapping: earner => the address of the entity who can call `processClaim` on behalf of the earner + function claimerFor(address earner) external view returns (address); + + /// @notice Mapping: claimer => token => total amount claimed + function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256); + + /// @notice the defautl split for all operators across all avss + function defaultOperatorSplitBips() external view returns (uint16); + + /// @notice the split for a specific `operator` for a specific `avs` + function getOperatorAVSSplit(address operator, address avs) external view returns (uint16); + + /// @notice the split for a specific `operator` for Programmatic Incentives + function getOperatorPISplit(address operator) external view returns (uint16); + + /// @notice return the hash of the earner's leaf + function calculateEarnerLeafHash(EarnerTreeMerkleLeaf calldata leaf) external pure returns (bytes32); + + /// @notice returns the hash of the earner's token leaf + function calculateTokenLeafHash(TokenTreeMerkleLeaf calldata leaf) external pure returns (bytes32); + + /// @notice returns 'true' if the claim would currently pass the check in `processClaims` + /// but will revert if not valid + function checkClaim(RewardsMerkleClaim calldata claim) external view returns (bool); + + /// @notice returns the number of distribution roots posted + function getDistributionRootsLength() external view returns (uint256); + + /// @notice returns the distributionRoot at the specified index + function getDistributionRootAtIndex(uint256 index) external view returns (DistributionRoot memory); + + /// @notice returns the current distributionRoot + function getCurrentDistributionRoot() external view returns (DistributionRoot memory); + + /// @notice loop through the distribution roots from reverse and get latest root that is not disabled and activated + /// i.e. a root that can be claimed against + function getCurrentClaimableDistributionRoot() external view returns (DistributionRoot memory); + + /// @notice loop through distribution roots from reverse and return index from hash + function getRootIndexFromHash(bytes32 rootHash) external view returns (uint32); + + /// @notice The address of the entity that can update the contract with new merkle roots + function rewardsUpdater() external view returns (address); + + /** + * @notice The interval in seconds at which the calculation for a RewardsSubmission distribution is done. + * @dev Rewards Submission durations must be multiples of this interval. + */ + function CALCULATION_INTERVAL_SECONDS() external view returns (uint32); + + /// @notice The maximum amount of time (seconds) that a RewardsSubmission can span over + function MAX_REWARDS_DURATION() external view returns (uint32); + + /// @notice max amount of time (seconds) that a submission can start in the past + function MAX_RETROACTIVE_LENGTH() external view returns (uint32); + + /// @notice max amount of time (seconds) that a submission can start in the future + function MAX_FUTURE_LENGTH() external view returns (uint32); + + /// @notice absolute min timestamp (seconds) that a submission can start at + function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IShareManager.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IShareManager.sol new file mode 100644 index 00000000..5bebed1b --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IShareManager.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "./IStrategy.sol"; + +/** + * @title Interface for a `IShareManager` contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice This contract is used by the DelegationManager as a unified interface to interact with the EigenPodManager and StrategyManager + */ +interface IShareManager { + /// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + function removeDepositShares(address staker, IStrategy strategy, uint256 depositSharesToRemove) external; + + /// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + /// @dev token is not validated when talking to the EigenPodManager + /// @return existingDepositShares the shares the staker had before any were added + /// @return addedShares the new shares added to the staker's balance + function addShares(address staker, IStrategy strategy, IERC20 token, uint256 shares) + external + returns (uint256, uint256); + + /// @notice Used by the DelegationManager to convert withdrawn descaled shares to tokens and send them to a staker + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + /// @dev token is not validated when talking to the EigenPodManager + function withdrawSharesAsTokens(address staker, IStrategy strategy, IERC20 token, uint256 shares) external; + + /// @notice Returns the current shares of `user` in `strategy` + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + /// @dev returns 0 if the user has negative shares + function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 depositShares); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/ISignatureUtils.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/ISignatureUtils.sol new file mode 100644 index 00000000..9e1ac2b4 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/ISignatureUtils.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +/** + * @title The interface for common signature utilities. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + */ +interface ISignatureUtils { + error InvalidSignature(); + error SignatureExpired(); + + // @notice Struct that bundles together a signature and an expiration time for the signature. Used primarily for stack management. + struct SignatureWithExpiry { + // the signature itself, formatted as a single bytes object + bytes signature; + // the expiration timestamp (UTC) of the signature + uint256 expiry; + } + + // @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management. + struct SignatureWithSaltAndExpiry { + // the signature itself, formatted as a single bytes object + bytes signature; + // the salt used to generate the signature + bytes32 salt; + // the expiration timestamp (UTC) of the signature + uint256 expiry; + } +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategy.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategy.sol new file mode 100644 index 00000000..037d37d3 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategy.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +/** + * @title Minimal interface for an `Strategy` contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice Custom `Strategy` implementations may expand extensively on this interface. + */ +interface IStrategy { + /** + * @notice convenience function for fetching the current underlying value of all of the `user`'s shares in + * this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications + */ + function userUnderlying(address user) external returns (uint256); + + /** + * @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy. + * @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications + * @param amountShares is the amount of shares to calculate its conversion into the underlying token + * @return The amount of shares corresponding to the input `amountUnderlying` + * @dev Implementation for these functions in particular may vary significantly for different strategies + */ + function sharesToUnderlyingView(uint256 amountShares) external view returns (uint256); + + /** + * @notice convenience function for fetching the current underlying value of all of the `user`'s shares in + * this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications + */ + function userUnderlyingView(address user) external view returns (uint256); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyFactory.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyFactory.sol new file mode 100644 index 00000000..fbad83a0 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyFactory.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "./IStrategy.sol"; + +/** + * @title Interface for the `StrategyFactory` contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @dev This may not be compatible with non-standard ERC20 tokens. Caution is warranted. + */ +interface IStrategyFactory { + /// @dev Thrown when attempting to deploy a strategy for a blacklisted token. + error BlacklistedToken(); + /// @dev Thrown when attempting to deploy a strategy that already exists. + error StrategyAlreadyExists(); + /// @dev Thrown when attempting to blacklist a token that is already blacklisted + error AlreadyBlacklisted(); + + event TokenBlacklisted(IERC20 token); + + /// @notice Upgradeable beacon which new Strategies deployed by this contract point to + function strategyBeacon() external view returns (IBeacon); + + /// @notice Mapping token => Strategy contract for the token + /// The strategies in this mapping are deployed by the StrategyFactory. + /// The factory can only deploy a single strategy per token address + /// These strategies MIGHT not be whitelisted in the StrategyManager, + /// though deployNewStrategy does whitelist by default. + /// These strategies MIGHT not be the only strategy for the underlying token + /// as additional strategies can be whitelisted by the owner of the factory. + function deployedStrategies(IERC20 token) external view returns (IStrategy); + + /** + * @notice Deploy a new strategyBeacon contract for the ERC20 token. + * @param token the token to deploy a strategy for + * @dev A strategy contract must not yet exist for the token. + * $dev Immense caution is warranted for non-standard ERC20 tokens, particularly "reentrant" tokens + * like those that conform to ERC777. + */ + function deployNewStrategy(IERC20 token) external returns (IStrategy newStrategy); + + /** + * @notice Owner-only function to pass through a call to `StrategyManager.addStrategiesToDepositWhitelist` + */ + function whitelistStrategies(IStrategy[] calldata strategiesToWhitelist) external; + + /** + * @notice Owner-only function to pass through a call to `StrategyManager.removeStrategiesFromDepositWhitelist` + */ + function removeStrategiesFromWhitelist(IStrategy[] calldata strategiesToRemoveFromWhitelist) external; + + /// @notice Emitted when the `strategyBeacon` is changed + event StrategyBeaconModified(IBeacon previousBeacon, IBeacon newBeacon); + + /// @notice Emitted whenever a slot is set in the `tokenStrategy` mapping + event StrategySetForToken(IERC20 token, IStrategy strategy); +} diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyManager.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyManager.sol new file mode 100644 index 00000000..067882f8 --- /dev/null +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IStrategyManager.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import "./IStrategy.sol"; +import "./IShareManager.sol"; +import "./IDelegationManager.sol"; +import "./IEigenPodManager.sol"; + +interface IStrategyManagerErrors { + /// @dev Thrown when total strategies deployed exceeds max. + error MaxStrategiesExceeded(); + /// @dev Thrown when call attempted from address that's not delegation manager. + error OnlyDelegationManager(); + /// @dev Thrown when call attempted from address that's not strategy whitelister. + error OnlyStrategyWhitelister(); + /// @dev Thrown when provided `shares` amount is too high. + error SharesAmountTooHigh(); + /// @dev Thrown when provided `shares` amount is zero. + error SharesAmountZero(); + /// @dev Thrown when provided `staker` address is null. + error StakerAddressZero(); + /// @dev Thrown when provided `strategy` not found. + error StrategyNotFound(); + /// @dev Thrown when attempting to deposit to a non-whitelisted strategy. + error StrategyNotWhitelisted(); +} + +interface IStrategyManagerEvents { + /** + * @notice Emitted when a new deposit occurs on behalf of `staker`. + * @param staker Is the staker who is depositing funds into EigenLayer. + * @param strategy Is the strategy that `staker` has deposited into. + * @param token Is the token that `staker` deposited. + * @param shares Is the number of new shares `staker` has been granted in `strategy`. + */ + event Deposit(address staker, IERC20 token, IStrategy strategy, uint256 shares); + + /// @notice Emitted when the `strategyWhitelister` is changed + event StrategyWhitelisterChanged(address previousAddress, address newAddress); + + /// @notice Emitted when a strategy is added to the approved list of strategies for deposit + event StrategyAddedToDepositWhitelist(IStrategy strategy); + + /// @notice Emitted when a strategy is removed from the approved list of strategies for deposit + event StrategyRemovedFromDepositWhitelist(IStrategy strategy); +} + +/** + * @title Interface for the primary entrypoint for funds into EigenLayer. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice See the `StrategyManager` contract itself for implementation details. + */ +interface IStrategyManager is IStrategyManagerErrors, IStrategyManagerEvents, IShareManager { + /** + * @notice Initializes the strategy manager contract. Sets the `pauserRegistry` (currently **not** modifiable after being set), + * and transfers contract ownership to the specified `initialOwner`. + * @param initialOwner Ownership of this contract is transferred to this address. + * @param initialStrategyWhitelister The initial value of `strategyWhitelister` to set. + * @param initialPausedStatus The initial value of `_paused` to set. + */ + function initialize(address initialOwner, address initialStrategyWhitelister, uint256 initialPausedStatus) + external; + + /** + * @notice Deposits `amount` of `token` into the specified `strategy`, with the resultant shares credited to `msg.sender` + * @param strategy is the specified strategy where deposit is to be made, + * @param token is the denomination in which the deposit is to be made, + * @param amount is the amount of token to be deposited in the strategy by the staker + * @return shares The amount of new shares in the `strategy` created as part of the action. + * @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf. + * @dev Cannot be called by an address that is 'frozen' (this function will revert if the `msg.sender` is frozen). + * + * WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors + * where the token balance and corresponding strategy shares are not in sync upon reentrancy. + */ + function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares); + + /** + * @notice Used for depositing an asset into the specified strategy with the resultant shares credited to `staker`, + * who must sign off on the action. + * Note that the assets are transferred out/from the `msg.sender`, not from the `staker`; this function is explicitly designed + * purely to help one address deposit 'for' another. + * @param strategy is the specified strategy where deposit is to be made, + * @param token is the denomination in which the deposit is to be made, + * @param amount is the amount of token to be deposited in the strategy by the staker + * @param staker the staker that the deposited assets will be credited to + * @param expiry the timestamp at which the signature expires + * @param signature is a valid signature from the `staker`. either an ECDSA signature if the `staker` is an EOA, or data to forward + * following EIP-1271 if the `staker` is a contract + * @return shares The amount of new shares in the `strategy` created as part of the action. + * @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf. + * @dev A signature is required for this function to eliminate the possibility of griefing attacks, specifically those + * targeting stakers who may be attempting to undelegate. + * + * WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors + * where the token balance and corresponding strategy shares are not in sync upon reentrancy + */ + function depositIntoStrategyWithSignature( + IStrategy strategy, + IERC20 token, + uint256 amount, + address staker, + uint256 expiry, + bytes memory signature + ) external returns (uint256 shares); + + /** + * @notice Burns Strategy shares for the given strategy by calling into the strategy to transfer to the default burn address. + * @param strategy The strategy to burn shares in. + * @param sharesToBurn The amount of shares to burn. + * @dev This function is only called by the DelegationManager when an operator is slashed. + */ + function burnShares(IStrategy strategy, uint256 sharesToBurn) external; + + /** + * @notice Owner-only function to change the `strategyWhitelister` address. + * @param newStrategyWhitelister new address for the `strategyWhitelister`. + */ + function setStrategyWhitelister(address newStrategyWhitelister) external; + + /** + * @notice Owner-only function that adds the provided Strategies to the 'whitelist' of strategies that stakers can deposit into + * @param strategiesToWhitelist Strategies that will be added to the `strategyIsWhitelistedForDeposit` mapping (if they aren't in it already) + */ + function addStrategiesToDepositWhitelist(IStrategy[] calldata strategiesToWhitelist) external; + + /** + * @notice Owner-only function that removes the provided Strategies from the 'whitelist' of strategies that stakers can deposit into + * @param strategiesToRemoveFromWhitelist Strategies that will be removed to the `strategyIsWhitelistedForDeposit` mapping (if they are in it) + */ + function removeStrategiesFromDepositWhitelist(IStrategy[] calldata strategiesToRemoveFromWhitelist) external; + + /// @notice Returns bool for whether or not `strategy` is whitelisted for deposit + function strategyIsWhitelistedForDeposit(IStrategy strategy) external view returns (bool); + + /** + * @notice Get all details on the staker's deposits and corresponding shares + * @return (staker's strategies, shares in these strategies) + */ + function getDeposits(address staker) external view returns (IStrategy[] memory, uint256[] memory); + + function getStakerStrategyList(address staker) external view returns (IStrategy[] memory); + + /// @notice Simple getter function that returns `stakerStrategyList[staker].length`. + function stakerStrategyListLength(address staker) external view returns (uint256); + + /// @notice Returns the current shares of `user` in `strategy` + function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 shares); + + /// @notice Returns the single, central Delegation contract of EigenLayer + function delegation() external view returns (IDelegationManager); + + /// @notice Returns the address of the `strategyWhitelister` + function strategyWhitelister() external view returns (address); + + /** + * @param staker The address of the staker. + * @param strategy The strategy to deposit into. + * @param token The token to deposit. + * @param amount The amount of `token` to deposit. + * @param nonce The nonce of the staker. + * @param expiry The expiry of the signature. + * @return The EIP-712 signable digest hash. + */ + function calculateStrategyDepositDigestHash( + address staker, + IStrategy strategy, + IERC20 token, + uint256 amount, + uint256 nonce, + uint256 expiry + ) external view returns (bytes32); +} diff --git a/mainnet-contracts/src/interface/IPufferModule.sol b/mainnet-contracts/src/interface/IPufferModule.sol index d7a2e442..4d72e9a3 100644 --- a/mainnet-contracts/src/interface/IPufferModule.sol +++ b/mainnet-contracts/src/interface/IPufferModule.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; -import { BeaconChainProofs } from "eigenlayer/libraries/BeaconChainProofs.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; +import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +// import { BeaconChainProofs } from "../interface/Eigenlayer-Slashing/BeaconChainProofs.sol"; +import { IDelegationManager } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IDelegationManagerTypes } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; /** * @title IPufferModule @@ -96,9 +97,8 @@ interface IPufferModule { * @notice Completes the queued withdrawals */ function completeQueuedWithdrawals( - IDelegationManager.Withdrawal[] calldata withdrawals, + IDelegationManagerTypes.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens ) external; diff --git a/mainnet-contracts/src/interface/IPufferModuleManager.sol b/mainnet-contracts/src/interface/IPufferModuleManager.sol index 5ad2f34d..c2711005 100644 --- a/mainnet-contracts/src/interface/IPufferModuleManager.sol +++ b/mainnet-contracts/src/interface/IPufferModuleManager.sol @@ -2,18 +2,17 @@ pragma solidity >=0.8.0 <0.9.0; import { IPufferModule } from "../interface/IPufferModule.sol"; -import { IRestakingOperator } from "../interface/IRestakingOperator.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; -import { BeaconChainProofs } from "eigenlayer/libraries/BeaconChainProofs.sol"; +import { IDelegationManager } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; - +import { RestakingOperator } from "src/RestakingOperator.sol"; /** * @title IPufferModuleManager * @author Puffer Finance * @custom:security-contact security@puffer.fi */ + interface IPufferModuleManager { /** * @notice Thrown if the module name is not allowed @@ -37,12 +36,10 @@ interface IPufferModuleManager { /** * @notice Emitted when the Restaking Operator is created * @param restakingOperator is the address of the restaking operator - * @param operatorDetails is the struct with new operator details - * @dev Signature "0xbb6c366230e589c402e164f680d07db88a6c1d4dda4dd2dcbab5528c09a6b046" + * @param delegationApprover is the address of the delegation approver + * @dev Signature "0x28682dddd8aa82d42ec7143a18beba2d09b27d4581f2f26a6afcd0da4576ae71" */ - event RestakingOperatorCreated( - address indexed restakingOperator, IDelegationManager.OperatorDetails operatorDetails - ); + event RestakingOperatorCreated(address indexed restakingOperator, address indexed delegationApprover); /** * @notice Emitted when the Restaking Operator is modified @@ -50,9 +47,7 @@ interface IPufferModuleManager { * @param newOperatorDetails is the struct with new operator details * @dev Signature "0xee78237d6444cc6c9083c1ef31a82b0feac23fbdf0cf52d7b0ed66dfa5f7f9f2" */ - event RestakingOperatorModified( - address indexed restakingOperator, IDelegationManager.OperatorDetails newOperatorDetails - ); + event RestakingOperatorModified(address indexed restakingOperator, address indexed newOperatorDetails); /** * @notice Emitted when the Withdrawals are queued @@ -118,58 +113,6 @@ interface IPufferModuleManager { */ event ProofSubmitterSet(bytes32 indexed moduleName, address indexed proofSubmitter); - /** - * @notice Emitted when the Restaking Operator is registered to an AVS - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param socket is the socket of the operator (typically an IP address) - * @dev Signature "0x4651591b511cac27601595cefbb19b2f0a04ec7b9348230f44a1309b9d70a8c9" - */ - event RestakingOperatorRegisteredToAVS( - IRestakingOperator restakingOperator, address avsRegistryCoordinator, bytes quorumNumbers, string socket - ); - - /** - * @notice Emitted when the Restaking Operator is registered to an AVS - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param socket is the socket of the operator (typically an IP address) - * @param operatorKickParams used to determine which operator is removed to maintain quorum capacity as the - * operator registers for quorums - * @dev Signature "0x4651591b511cac27601595cefbb19b2f0a04ec7b9348230f44a1309b9d70a8c9" - */ - event RestakingOperatorRegisteredToAVSWithChurn( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - bytes quorumNumbers, - string socket, - IRegistryCoordinator.OperatorKickParam[] operatorKickParams - ); - - /** - * @notice Emitted when the Restaking Operator is deregistered from an AVS - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from - * @dev Signature "0x4651591b511cac27601595cefbb19b2f0a04ec7b9348230f44a1309b9d70a8c9" - */ - event RestakingOperatorDeregisteredFromAVS( - IRestakingOperator restakingOperator, address avsRegistryCoordinator, bytes quorumNumbers - ); - - /** - * @notice Emitted when the Restaking Operator AVS Socket is updated - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param socket is the new socket of the operator - * @dev Signature "0x4651591b511cac27601595cefbb19b2f0a04ec7b9348230f44a1309b9d70a8c9" - */ - event RestakingOperatorAVSSocketUpdated( - IRestakingOperator restakingOperator, address avsRegistryCoordinator, string socket - ); - /** * @notice Emitted when the Restaking Operator or PufferModule sets the calimer to `claimer` * @dev Signature "0x4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a28" @@ -213,7 +156,7 @@ interface IPufferModuleManager { string memory metadataURI, address delegationApprover, uint32 stakerOptOutWindowBlocks - ) external returns (IRestakingOperator module); + ) external returns (RestakingOperator module); /** * @notice Create a new Puffer module @@ -242,7 +185,7 @@ interface IPufferModuleManager { * @dev Restricted to the DAO */ function callModifyOperatorDetails( - IRestakingOperator restakingOperator, + RestakingOperator restakingOperator, IDelegationManager.OperatorDetails calldata newOperatorDetails ) external; @@ -271,7 +214,7 @@ interface IPufferModuleManager { * @param slasher is the address of the slasher contract to opt into * @dev Restricted to the DAO */ - function callOptIntoSlashing(IRestakingOperator restakingOperator, address slasher) external; + function callOptIntoSlashing(RestakingOperator restakingOperator, address slasher) external; /** * @notice Calls the updateOperatorMetadataURI function on the restaking operator @@ -279,7 +222,7 @@ interface IPufferModuleManager { * @param metadataURI is the URI of the operator's metadata * @dev Restricted to the DAO */ - function callUpdateMetadataURI(IRestakingOperator restakingOperator, string calldata metadataURI) external; + function callUpdateMetadataURI(RestakingOperator restakingOperator, string calldata metadataURI) external; /** * @notice Calls the callDelegateTo function on the target module @@ -311,97 +254,8 @@ interface IPufferModuleManager { * @dev Restricted to the DAO */ function updateAVSRegistrationSignatureProof( - IRestakingOperator restakingOperator, + RestakingOperator restakingOperator, bytes32 digestHash, address signer ) external; - - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum - * operator capacity after the operator is registered, this method will fail. - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param socket is the socket of the operator (typically an IP address) - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev Restricted to the DAO - */ - function callRegisterOperatorToAVS( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature - ) external; - - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator - * capacity, `operatorKickParams` is used to replace an old operator with the new one. - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param socket is the socket of the operator (typically an IP address) - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorKickParams used to determine which operator is removed to maintain quorum capacity as the - * operator registers for quorums - * @param churnApproverSignature is the signature of the churnApprover over the `operatorKickParams` - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev Restricted to the DAO - */ - function callRegisterOperatorToAVSWithChurn( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - IRegistryCoordinator.OperatorKickParam[] calldata operatorKickParams, - ISignatureUtils.SignatureWithSaltAndExpiry calldata churnApproverSignature, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature - ) external; - - /** - * @notice Deregisters the caller from one or more quorums - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from - * @dev Restricted to the DAO - */ - function callDeregisterOperatorFromAVS( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - bytes calldata quorumNumbers - ) external; - - /** - * @notice Updates the socket of the msg.sender given they are a registered operator - * @param restakingOperator is the address of the restaking operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param socket is the new socket of the operator - * @dev Restricted to the DAO - */ - function callUpdateOperatorAVSSocket( - IRestakingOperator restakingOperator, - address avsRegistryCoordinator, - string memory socket - ) external; - - /** - * @notice Calls the `callSetClaimerFor` function on the target module or restaking operator contract - * @param moduleOrReOp is the address of the target module or restaking operator contract - * @param claimer is the address of the claimer to be set - * @dev Restricted to the DAO - */ - function callSetClaimerFor(address moduleOrReOp, address claimer) external; - - /** - * @notice Calls the `target` contract with `customCalldata` from the Restaking Operator contract - * @param restakingOperator is the Restaking Operator contract - * @param target is the address of the target contract that ReOp will call - * @param customCalldata is the calldata to be passed to the target contract - * @dev Restricted to the DAO - */ - function customExternalCall(IRestakingOperator restakingOperator, address target, bytes calldata customCalldata) - external; } diff --git a/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol b/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol index 8015b643..d078ab52 100644 --- a/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol +++ b/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; +import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; interface IRegistryCoordinatorExtended is IRegistryCoordinator { /** diff --git a/mainnet-contracts/src/interface/IRestakingOperator.sol b/mainnet-contracts/src/interface/IRestakingOperator.sol deleted file mode 100644 index 6ad01c72..00000000 --- a/mainnet-contracts/src/interface/IRestakingOperator.sol +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISlasher } from "eigenlayer/interfaces/ISlasher.sol"; -import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; - -/** - * @title IRestakingOperator - * @author Puffer Finance - * @custom:security-contact security@puffer.fi - */ -interface IRestakingOperator { - /** - * @notice Returns the EigenLayer's DelegationManager - */ - function EIGEN_DELEGATION_MANAGER() external view returns (IDelegationManager); - - /** - * @notice Returns the EigenLayer's Slasher - */ - function EIGEN_SLASHER() external view returns (ISlasher); - - /** - * @notice Modify the operator details - * @param newOperatorDetails is the struct with new operator details - * @dev Restricted to the PufferModuleManager - */ - function modifyOperatorDetails(IDelegationManager.OperatorDetails calldata newOperatorDetails) external; - - /** - * @notice Opts the restaking operator into slashing by the slasher - * @param slasher is the address of the slasher contract to opt into - * @dev Restricted to the PufferModuleManager - */ - function optIntoSlashing(address slasher) external; - - /** - * @notice Updates the operator's metadata URI - * @param metadataURI is the URI of the operator's metadata - * @dev Restricted to the PufferModuleManager - */ - function updateOperatorMetadataURI(string calldata metadataURI) external; - - /** - * @notice Updates a signature proof by setting the signer address of the message hash - * @param digestHash is message hash - * @param signer is the signer address - * @dev Restricted to the PufferModuleManager - */ - function updateSignatureProof(bytes32 digestHash, address signer) external; - - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum - * operator capacity after the operator is registered, this method will fail. - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param socket is the socket of the operator (typically an IP address) - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ - function registerOperatorToAVS( - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature - ) external; - - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator - * capacity, `operatorKickParams` is used to replace an old operator with the new one. - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorKickParams used to determine which operator is removed to maintain quorum capacity as the - * operator registers for quorums - * @param churnApproverSignature is the signature of the churnApprover over the `operatorKickParams` - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ - function registerOperatorToAVSWithChurn( - address avsRegistryCoordinator, - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - IRegistryCoordinator.OperatorKickParam[] calldata operatorKickParams, - ISignatureUtils.SignatureWithSaltAndExpiry calldata churnApproverSignature, - ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature - ) external; - - /** - * @notice Does a custom call to `target` with `customCalldata` - * @return response - */ - function customCalldataCall(address target, bytes calldata customCalldata) - external - returns (bytes memory response); - - /** - * @notice Deregisters the caller from one or more quorums - * @param avsRegistryCoordinator the avs registry coordinator address - * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from - */ - function deregisterOperatorFromAVS(address avsRegistryCoordinator, bytes calldata quorumNumbers) external; - - /** - * @notice Updates the socket of the msg.sender given they are a registered operator - * @param avsRegistryCoordinator the avs registry coordinator address - * @param socket is the new socket of the operator - */ - function updateOperatorAVSSocket(address avsRegistryCoordinator, string memory socket) external; - - /** - * @notice Sets the rewards claimer to `claimer` for the RestakingOperator - */ - function callSetClaimerFor(address claimer) external; -} diff --git a/mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol b/mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol new file mode 100644 index 00000000..880efcff --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "./Merkle.sol"; +import "../libraries/Endian.sol"; + +//Utility library for parsing and PHASE0 beacon chain block headers +//SSZ Spec: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization +//BeaconBlockHeader Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader +//BeaconState Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconstate +library BeaconChainProofs { + /// @dev Thrown when a proof is invalid. + error InvalidProof(); + /// @dev Thrown when a proof with an invalid length is provided. + error InvalidProofLength(); + /// @dev Thrown when a validator fields length is invalid. + error InvalidValidatorFieldsLength(); + + /// @notice Heights of various merkle trees in the beacon chain + /// - beaconBlockRoot + /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT + /// -- beaconStateRoot + /// | HEIGHT: BEACON_STATE_TREE_HEIGHT + /// validatorContainerRoot, balanceContainerRoot + /// | | HEIGHT: BALANCE_TREE_HEIGHT + /// | individual balances + /// | HEIGHT: VALIDATOR_TREE_HEIGHT + /// individual validators + uint256 internal constant BEACON_BLOCK_HEADER_TREE_HEIGHT = 3; + uint256 internal constant BEACON_STATE_TREE_HEIGHT = 5; + uint256 internal constant BALANCE_TREE_HEIGHT = 38; + uint256 internal constant VALIDATOR_TREE_HEIGHT = 40; + + /// @notice Index of the beaconStateRoot in the `BeaconBlockHeader` container + /// + /// BeaconBlockHeader = [..., state_root, ...] + /// 0... 3 + /// + /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader) + uint256 internal constant STATE_ROOT_INDEX = 3; + + /// @notice Indices for fields in the `BeaconState` container + /// + /// BeaconState = [..., validators, balances, ...] + /// 0... 11 12 + /// + /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate) + uint256 internal constant VALIDATOR_CONTAINER_INDEX = 11; + uint256 internal constant BALANCE_CONTAINER_INDEX = 12; + + /// @notice Number of fields in the `Validator` container + /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator) + uint256 internal constant VALIDATOR_FIELDS_LENGTH = 8; + + /// @notice Indices for fields in the `Validator` container + uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0; + uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1; + uint256 internal constant VALIDATOR_BALANCE_INDEX = 2; + uint256 internal constant VALIDATOR_SLASHED_INDEX = 3; + uint256 internal constant VALIDATOR_ACTIVATION_EPOCH_INDEX = 5; + uint256 internal constant VALIDATOR_EXIT_EPOCH_INDEX = 6; + + /// @notice Slot/Epoch timings + uint64 internal constant SECONDS_PER_SLOT = 12; + uint64 internal constant SLOTS_PER_EPOCH = 32; + uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT; + + /// @notice `FAR_FUTURE_EPOCH` is used as the default value for certain `Validator` + /// fields when a `Validator` is first created on the beacon chain + uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max; + bytes8 internal constant UINT64_MASK = 0xffffffffffffffff; + + /// @notice Contains a beacon state root and a merkle proof verifying its inclusion under a beacon block root + struct StateRootProof { + bytes32 beaconStateRoot; + bytes proof; + } + + /// @notice Contains a validator's fields and a merkle proof of their inclusion under a beacon state root + struct ValidatorProof { + bytes32[] validatorFields; + bytes proof; + } + + /// @notice Contains a beacon balance container root and a proof of this root under a beacon block root + struct BalanceContainerProof { + bytes32 balanceContainerRoot; + bytes proof; + } + + /// @notice Contains a validator balance root and a proof of its inclusion under a balance container root + struct BalanceProof { + bytes32 pubkeyHash; + bytes32 balanceRoot; + bytes proof; + } + + /** + * + * VALIDATOR FIELDS -> BEACON STATE ROOT -> BEACON BLOCK ROOT + * + */ + + /// @notice Verify a merkle proof of the beacon state root against a beacon block root + /// @param beaconBlockRoot merkle root of the beacon block + /// @param proof the beacon state root and merkle proof of its inclusion under `beaconBlockRoot` + function verifyStateRoot(bytes32 beaconBlockRoot, StateRootProof calldata proof) internal view { + require(proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT), InvalidProofLength()); + + /// This merkle proof verifies the `beaconStateRoot` under the `beaconBlockRoot` + /// - beaconBlockRoot + /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT + /// -- beaconStateRoot + require( + Merkle.verifyInclusionSha256({ + proof: proof.proof, + root: beaconBlockRoot, + leaf: proof.beaconStateRoot, + index: STATE_ROOT_INDEX + }), + InvalidProof() + ); + } + + /// @notice Verify a merkle proof of a validator container against a `beaconStateRoot` + /// @dev This proof starts at a validator's container root, proves through the validator container root, + /// and continues proving to the root of the `BeaconState` + /// @dev See https://eth2book.info/capella/part3/containers/dependencies/#validator for info on `Validator` containers + /// @dev See https://eth2book.info/capella/part3/containers/state/#beaconstate for info on `BeaconState` containers + /// @param beaconStateRoot merkle root of the `BeaconState` container + /// @param validatorFields an individual validator's fields. These are merklized to form a `validatorRoot`, + /// which is used as the leaf to prove against `beaconStateRoot` + /// @param validatorFieldsProof a merkle proof of inclusion of `validatorFields` under `beaconStateRoot` + /// @param validatorIndex the validator's unique index + function verifyValidatorFields( + bytes32 beaconStateRoot, + bytes32[] calldata validatorFields, + bytes calldata validatorFieldsProof, + uint40 validatorIndex + ) internal view { + require(validatorFields.length == VALIDATOR_FIELDS_LENGTH, InvalidValidatorFieldsLength()); + + /// Note: the reason we use `VALIDATOR_TREE_HEIGHT + 1` here is because the merklization process for + /// this container includes hashing the root of the validator tree with the length of the validator list + require( + validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_TREE_HEIGHT), + InvalidProofLength() + ); + + // Merkleize `validatorFields` to get the leaf to prove + bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields); + + /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees: + /// - beaconStateRoot + /// | HEIGHT: BEACON_STATE_TREE_HEIGHT + /// -- validatorContainerRoot + /// | HEIGHT: VALIDATOR_TREE_HEIGHT + 1 + /// ---- validatorRoot + uint256 index = (VALIDATOR_CONTAINER_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) | uint256(validatorIndex); + + require( + Merkle.verifyInclusionSha256({ + proof: validatorFieldsProof, + root: beaconStateRoot, + leaf: validatorRoot, + index: index + }), + InvalidProof() + ); + } + + /** + * + * VALIDATOR BALANCE -> BALANCE CONTAINER ROOT -> BEACON BLOCK ROOT + * + */ + + /// @notice Verify a merkle proof of the beacon state's balances container against the beacon block root + /// @dev This proof starts at the balance container root, proves through the beacon state root, and + /// continues proving through the beacon block root. As a result, this proof will contain elements + /// of a `StateRootProof` under the same block root, with the addition of proving the balances field + /// within the beacon state. + /// @dev This is used to make checkpoint proofs more efficient, as a checkpoint will verify multiple balances + /// against the same balance container root. + /// @param beaconBlockRoot merkle root of the beacon block + /// @param proof a beacon balance container root and merkle proof of its inclusion under `beaconBlockRoot` + function verifyBalanceContainer(bytes32 beaconBlockRoot, BalanceContainerProof calldata proof) internal view { + require( + proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT + BEACON_STATE_TREE_HEIGHT), + InvalidProofLength() + ); + + /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees: + /// - beaconBlockRoot + /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT + /// -- beaconStateRoot + /// | HEIGHT: BEACON_STATE_TREE_HEIGHT + /// ---- balancesContainerRoot + uint256 index = (STATE_ROOT_INDEX << (BEACON_STATE_TREE_HEIGHT)) | BALANCE_CONTAINER_INDEX; + + require( + Merkle.verifyInclusionSha256({ + proof: proof.proof, + root: beaconBlockRoot, + leaf: proof.balanceContainerRoot, + index: index + }), + InvalidProof() + ); + } + + /// @notice Verify a merkle proof of a validator's balance against the beacon state's `balanceContainerRoot` + /// @param balanceContainerRoot the merkle root of all validators' current balances + /// @param validatorIndex the index of the validator whose balance we are proving + /// @param proof the validator's associated balance root and a merkle proof of inclusion under `balanceContainerRoot` + /// @return validatorBalanceGwei the validator's current balance (in gwei) + function verifyValidatorBalance(bytes32 balanceContainerRoot, uint40 validatorIndex, BalanceProof calldata proof) + internal + view + returns (uint64 validatorBalanceGwei) + { + /// Note: the reason we use `BALANCE_TREE_HEIGHT + 1` here is because the merklization process for + /// this container includes hashing the root of the balances tree with the length of the balances list + require(proof.proof.length == 32 * (BALANCE_TREE_HEIGHT + 1), InvalidProofLength()); + + /// When merkleized, beacon chain balances are combined into groups of 4 called a `balanceRoot`. The merkle + /// proof here verifies that this validator's `balanceRoot` is included in the `balanceContainerRoot` + /// - balanceContainerRoot + /// | HEIGHT: BALANCE_TREE_HEIGHT + /// -- balanceRoot + uint256 balanceIndex = uint256(validatorIndex / 4); + + require( + Merkle.verifyInclusionSha256({ + proof: proof.proof, + root: balanceContainerRoot, + leaf: proof.balanceRoot, + index: balanceIndex + }), + InvalidProof() + ); + + /// Extract the individual validator's balance from the `balanceRoot` + return getBalanceAtIndex(proof.balanceRoot, validatorIndex); + } + + /** + * @notice Parses a balanceRoot to get the uint64 balance of a validator. + * @dev During merkleization of the beacon state balance tree, four uint64 values are treated as a single + * leaf in the merkle tree. We use validatorIndex % 4 to determine which of the four uint64 values to + * extract from the balanceRoot. + * @param balanceRoot is the combination of 4 validator balances being proven for + * @param validatorIndex is the index of the validator being proven for + * @return The validator's balance, in Gwei + */ + function getBalanceAtIndex(bytes32 balanceRoot, uint40 validatorIndex) internal pure returns (uint64) { + uint256 bitShiftAmount = (validatorIndex % 4) * 64; + return Endian.fromLittleEndianUint64(bytes32((uint256(balanceRoot) << bitShiftAmount))); + } + + /// @notice Indices for fields in the `Validator` container: + /// 0: pubkey + /// 1: withdrawal credentials + /// 2: effective balance + /// 3: slashed? + /// 4: activation eligibility epoch + /// 5: activation epoch + /// 6: exit epoch + /// 7: withdrawable epoch + /// + /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator) + + /// @dev Retrieves a validator's pubkey hash + function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) { + return validatorFields[VALIDATOR_PUBKEY_INDEX]; + } + + /// @dev Retrieves a validator's withdrawal credentials + function getWithdrawalCredentials(bytes32[] memory validatorFields) internal pure returns (bytes32) { + return validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX]; + } + + /// @dev Retrieves a validator's effective balance (in gwei) + function getEffectiveBalanceGwei(bytes32[] memory validatorFields) internal pure returns (uint64) { + return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]); + } + + /// @dev Retrieves a validator's activation epoch + function getActivationEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) { + return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_ACTIVATION_EPOCH_INDEX]); + } + + /// @dev Retrieves true IFF a validator is marked slashed + function isValidatorSlashed(bytes32[] memory validatorFields) internal pure returns (bool) { + return validatorFields[VALIDATOR_SLASHED_INDEX] != 0; + } + + /// @dev Retrieves a validator's exit epoch + function getExitEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) { + return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_EXIT_EPOCH_INDEX]); + } +} diff --git a/mainnet-contracts/src/interface/libraries/BytesLib.sol b/mainnet-contracts/src/interface/libraries/BytesLib.sol new file mode 100644 index 00000000..81e3d35f --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/BytesLib.sol @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: Unlicense +/* + * @title Solidity Bytes Arrays Utils + * @author Gonçalo Sá + * + * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. + * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. + */ +pragma solidity >=0.8.0 <0.9.0; + +library BytesLib { + error Overflow(); + error OutOfBounds(); + + function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { + bytes memory tempBytes; + + assembly { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // Store the length of the first bytes array at the beginning of + // the memory for tempBytes. + let length := mload(_preBytes) + mstore(tempBytes, length) + + // Maintain a memory counter for the current write location in the + // temp bytes array by adding the 32 bytes for the array length to + // the starting location. + let mc := add(tempBytes, 0x20) + // Stop copying when the memory counter reaches the length of the + // first bytes array. + let end := add(mc, length) + + for { + // Initialize a copy counter to the start of the _preBytes data, + // 32 bytes into its memory. + let cc := add(_preBytes, 0x20) + } lt(mc, end) { + // Increase both counters by 32 bytes each iteration. + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + // Write the _preBytes data into the tempBytes memory 32 bytes + // at a time. + mstore(mc, mload(cc)) + } + + // Add the length of _postBytes to the current length of tempBytes + // and store it as the new length in the first 32 bytes of the + // tempBytes memory. + length := mload(_postBytes) + mstore(tempBytes, add(length, mload(tempBytes))) + + // Move the memory counter back from a multiple of 0x20 to the + // actual end of the _preBytes data. + mc := end + // Stop copying when the memory counter reaches the new combined + // length of the arrays. + end := add(mc, length) + + for { let cc := add(_postBytes, 0x20) } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { mstore(mc, mload(cc)) } + + // Update the free-memory pointer by padding our last write location + // to 32 bytes: add 31 bytes to the end of tempBytes to move to the + // next 32 byte block, then round down to the nearest multiple of + // 32. If the sum of the length of the two arrays is zero then add + // one before rounding down to leave a blank 32 bytes (the length block with 0). + mstore( + 0x40, + and( + add(add(end, iszero(add(length, mload(_preBytes)))), 31), + not(31) // Round down to the nearest 32 bytes. + ) + ) + } + + return tempBytes; + } + + function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { + assembly { + // Read the first 32 bytes of _preBytes storage, which is the length + // of the array. (We don't need to use the offset into the slot + // because arrays use the entire slot.) + let fslot := sload(_preBytes.slot) + // Arrays of 31 bytes or less have an even value in their slot, + // while longer arrays have an odd value. The actual length is + // the slot divided by two for odd values, and the lowest order + // byte divided by two for even values. + // If the slot is even, bitwise and the slot with 255 and divide by + // two to get the length. If the slot is odd, bitwise and the slot + // with -1 and divide by two. + let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) + let mlength := mload(_postBytes) + let newlength := add(slength, mlength) + // slength can contain both the length and contents of the array + // if length < 32 bytes so let's prepare for that + // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage + switch add(lt(slength, 32), lt(newlength, 32)) + case 2 { + // Since the new array still fits in the slot, we just need to + // update the contents of the slot. + // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length + sstore( + _preBytes.slot, + // all the modifications to the slot are inside this + // next block + add( + // we can just add to the slot contents because the + // bytes we want to change are the LSBs + fslot, + add( + mul( + div( + // load the bytes from memory + mload(add(_postBytes, 0x20)), + // zero all bytes to the right + exp(0x100, sub(32, mlength)) + ), + // and now shift left the number of bytes to + // leave space for the length in the slot + exp(0x100, sub(32, newlength)) + ), + // increase length by the double of the memory + // bytes length + mul(mlength, 2) + ) + ) + ) + } + case 1 { + // The stored value fits in the slot, but the combined value + // will exceed it. + // get the keccak hash to get the contents of the array + mstore(0x0, _preBytes.slot) + let sc := add(keccak256(0x0, 0x20), div(slength, 32)) + + // save new length + sstore(_preBytes.slot, add(mul(newlength, 2), 1)) + + // The contents of the _postBytes array start 32 bytes into + // the structure. Our first read should obtain the `submod` + // bytes that can fit into the unused space in the last word + // of the stored array. To get this, we read 32 bytes starting + // from `submod`, so the data we read overlaps with the array + // contents by `submod` bytes. Masking the lowest-order + // `submod` bytes allows us to add that value directly to the + // stored value. + + let submod := sub(32, slength) + let mc := add(_postBytes, submod) + let end := add(_postBytes, mlength) + let mask := sub(exp(0x100, submod), 1) + + sstore( + sc, + add( + and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00), + and(mload(mc), mask) + ) + ) + + for { + mc := add(mc, 0x20) + sc := add(sc, 1) + } lt(mc, end) { + sc := add(sc, 1) + mc := add(mc, 0x20) + } { sstore(sc, mload(mc)) } + + mask := exp(0x100, sub(mc, end)) + + sstore(sc, mul(div(mload(mc), mask), mask)) + } + default { + // get the keccak hash to get the contents of the array + mstore(0x0, _preBytes.slot) + // Start copying to the last used word of the stored array. + let sc := add(keccak256(0x0, 0x20), div(slength, 32)) + + // save new length + sstore(_preBytes.slot, add(mul(newlength, 2), 1)) + + // Copy over the first `submod` bytes of the new data as in + // case 1 above. + let slengthmod := mod(slength, 32) + // solhint-disable-next-line no-unused-vars + let mlengthmod := mod(mlength, 32) + let submod := sub(32, slengthmod) + let mc := add(_postBytes, submod) + let end := add(_postBytes, mlength) + let mask := sub(exp(0x100, submod), 1) + + sstore(sc, add(sload(sc), and(mload(mc), mask))) + + for { + sc := add(sc, 1) + mc := add(mc, 0x20) + } lt(mc, end) { + sc := add(sc, 1) + mc := add(mc, 0x20) + } { sstore(sc, mload(mc)) } + + mask := exp(0x100, sub(mc, end)) + + sstore(sc, mul(div(mload(mc), mask), mask)) + } + } + } + + function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { + require(_length + 31 >= _length, Overflow()); + require(_bytes.length >= _start + _length, OutOfBounds()); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { mstore(mc, mload(cc)) } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + + function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + require(_bytes.length >= _start + 20, OutOfBounds()); + address tempAddress; + + assembly { + tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) + } + + return tempAddress; + } + + function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { + require(_bytes.length >= _start + 1, OutOfBounds()); + uint8 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x1), _start)) + } + + return tempUint; + } + + function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { + require(_bytes.length >= _start + 2, OutOfBounds()); + uint16 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x2), _start)) + } + + return tempUint; + } + + function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { + require(_bytes.length >= _start + 4, OutOfBounds()); + uint32 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x4), _start)) + } + + return tempUint; + } + + function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { + require(_bytes.length >= _start + 8, OutOfBounds()); + uint64 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x8), _start)) + } + + return tempUint; + } + + function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { + require(_bytes.length >= _start + 12, OutOfBounds()); + uint96 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0xc), _start)) + } + + return tempUint; + } + + function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { + require(_bytes.length >= _start + 16, OutOfBounds()); + uint128 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x10), _start)) + } + + return tempUint; + } + + function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { + require(_bytes.length >= _start + 32, OutOfBounds()); + uint256 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x20), _start)) + } + + return tempUint; + } + + function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { + require(_bytes.length >= _start + 32, OutOfBounds()); + bytes32 tempBytes32; + + assembly { + tempBytes32 := mload(add(add(_bytes, 0x20), _start)) + } + + return tempBytes32; + } + + function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { + bool success = true; + + assembly { + let length := mload(_preBytes) + + // if lengths don't match the arrays are not equal + switch eq(length, mload(_postBytes)) + case 1 { + // cb is a circuit breaker in the for loop since there's + // no said feature for inline assembly loops + // cb = 1 - don't breaker + // cb = 0 - break + let cb := 1 + + let mc := add(_preBytes, 0x20) + let end := add(mc, length) + + for { let cc := add(_postBytes, 0x20) } // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: + eq(add(lt(mc, end), cb), 2) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + // if any of these checks fails then arrays are not equal + if iszero(eq(mload(mc), mload(cc))) { + // unsuccess: + success := 0 + cb := 0 + } + } + } + default { + // unsuccess: + success := 0 + } + } + + return success; + } + + function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { + bool success = true; + + assembly { + // we know _preBytes_offset is 0 + let fslot := sload(_preBytes.slot) + // Decode the length of the stored array like in concatStorage(). + let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) + let mlength := mload(_postBytes) + + // if lengths don't match the arrays are not equal + switch eq(slength, mlength) + case 1 { + // slength can contain both the length and contents of the array + // if length < 32 bytes so let's prepare for that + // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage + if iszero(iszero(slength)) { + switch lt(slength, 32) + case 1 { + // blank the last byte which is the length + fslot := mul(div(fslot, 0x100), 0x100) + + if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { + // unsuccess: + success := 0 + } + } + default { + // cb is a circuit breaker in the for loop since there's + // no said feature for inline assembly loops + // cb = 1 - don't breaker + // cb = 0 - break + let cb := 1 + + // get the keccak hash to get the contents of the array + mstore(0x0, _preBytes.slot) + let sc := keccak256(0x0, 0x20) + + let mc := add(_postBytes, 0x20) + let end := add(mc, mlength) + + // the next line is the loop condition: + // while(uint256(mc < end) + cb == 2) + // solhint-disable-next-line no-empty-blocks + for { } eq(add(lt(mc, end), cb), 2) { + sc := add(sc, 1) + mc := add(mc, 0x20) + } { + if iszero(eq(sload(sc), mload(mc))) { + // unsuccess: + success := 0 + cb := 0 + } + } + } + } + } + default { + // unsuccess: + success := 0 + } + } + + return success; + } +} diff --git a/mainnet-contracts/src/interface/libraries/Endian.sol b/mainnet-contracts/src/interface/libraries/Endian.sol new file mode 100644 index 00000000..f9799bb9 --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/Endian.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +library Endian { + /** + * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64 + * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type + * @return n The big endian-formatted uint64 + * @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) + * through a right-shift/shr operation. + */ + function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) { + // the number needs to be stored in little-endian encoding (ie in bytes 0-8) + n = uint64(uint256(lenum >> 192)); + // forgefmt: disable-next-item + return (n >> 56) | + ((0x00FF000000000000 & n) >> 40) | + ((0x0000FF0000000000 & n) >> 24) | + ((0x000000FF00000000 & n) >> 8) | + ((0x00000000FF000000 & n) << 8) | + ((0x0000000000FF0000 & n) << 24) | + ((0x000000000000FF00 & n) << 40) | + ((0x00000000000000FF & n) << 56); + } +} diff --git a/mainnet-contracts/src/interface/libraries/Merkle.sol b/mainnet-contracts/src/interface/libraries/Merkle.sol new file mode 100644 index 00000000..706b7b49 --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/Merkle.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol) + +pragma solidity ^0.8.0; + +/** + * @dev These functions deal with verification of Merkle Tree proofs. + * + * The tree and the proofs can be generated using our + * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. + * You will find a quickstart guide in the readme. + * + * WARNING: You should avoid using leaf values that are 64 bytes long prior to + * hashing, or use a hash function other than keccak256 for hashing leaves. + * This is because the concatenation of a sorted pair of internal nodes in + * the merkle tree could be reinterpreted as a leaf value. + * OpenZeppelin's JavaScript library generates merkle trees that are safe + * against this attack out of the box. + */ +library Merkle { + error InvalidProofLength(); + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. The tree is built assuming `leaf` is + * the 0 indexed `index`'th leaf from the bottom left of the tree. + * + * Note this is for a Merkle tree using the keccak/sha3 hash function + */ + function verifyInclusionKeccak(bytes memory proof, bytes32 root, bytes32 leaf, uint256 index) + internal + pure + returns (bool) + { + return processInclusionProofKeccak(proof, leaf, index) == root; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. The tree is built assuming `leaf` is + * the 0 indexed `index`'th leaf from the bottom left of the tree. + * @dev If the proof length is 0 then the leaf hash is returned. + * + * _Available since v4.4._ + * + * Note this is for a Merkle tree using the keccak/sha3 hash function + */ + function processInclusionProofKeccak(bytes memory proof, bytes32 leaf, uint256 index) + internal + pure + returns (bytes32) + { + require(proof.length % 32 == 0, InvalidProofLength()); + bytes32 computedHash = leaf; + for (uint256 i = 32; i <= proof.length; i += 32) { + if (index % 2 == 0) { + // if ith bit of index is 0, then computedHash is a left sibling + assembly { + mstore(0x00, computedHash) + mstore(0x20, mload(add(proof, i))) + computedHash := keccak256(0x00, 0x40) + index := div(index, 2) + } + } else { + // if ith bit of index is 1, then computedHash is a right sibling + assembly { + mstore(0x00, mload(add(proof, i))) + mstore(0x20, computedHash) + computedHash := keccak256(0x00, 0x40) + index := div(index, 2) + } + } + } + return computedHash; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. The tree is built assuming `leaf` is + * the 0 indexed `index`'th leaf from the bottom left of the tree. + * + * Note this is for a Merkle tree using the sha256 hash function + */ + function verifyInclusionSha256(bytes memory proof, bytes32 root, bytes32 leaf, uint256 index) + internal + view + returns (bool) + { + return processInclusionProofSha256(proof, leaf, index) == root; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. The tree is built assuming `leaf` is + * the 0 indexed `index`'th leaf from the bottom left of the tree. + * + * _Available since v4.4._ + * + * Note this is for a Merkle tree using the sha256 hash function + */ + function processInclusionProofSha256(bytes memory proof, bytes32 leaf, uint256 index) + internal + view + returns (bytes32) + { + require(proof.length != 0 && proof.length % 32 == 0, InvalidProofLength()); + bytes32[1] memory computedHash = [leaf]; + for (uint256 i = 32; i <= proof.length; i += 32) { + if (index % 2 == 0) { + // if ith bit of index is 0, then computedHash is a left sibling + assembly { + mstore(0x00, mload(computedHash)) + mstore(0x20, mload(add(proof, i))) + if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) } + index := div(index, 2) + } + } else { + // if ith bit of index is 1, then computedHash is a right sibling + assembly { + mstore(0x00, mload(add(proof, i))) + mstore(0x20, mload(computedHash)) + if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) } + index := div(index, 2) + } + } + } + return computedHash[0]; + } + + /** + * @notice this function returns the merkle root of a tree created from a set of leaves using sha256 as its hash function + * @param leaves the leaves of the merkle tree + * @return The computed Merkle root of the tree. + * @dev A pre-condition to this function is that leaves.length is a power of two. If not, the function will merkleize the inputs incorrectly. + */ + function merkleizeSha256(bytes32[] memory leaves) internal pure returns (bytes32) { + //there are half as many nodes in the layer above the leaves + uint256 numNodesInLayer = leaves.length / 2; + //create a layer to store the internal nodes + bytes32[] memory layer = new bytes32[](numNodesInLayer); + //fill the layer with the pairwise hashes of the leaves + for (uint256 i = 0; i < numNodesInLayer; i++) { + layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])); + } + //the next layer above has half as many nodes + numNodesInLayer /= 2; + //while we haven't computed the root + while (numNodesInLayer != 0) { + //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children + for (uint256 i = 0; i < numNodesInLayer; i++) { + layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1])); + } + //the next layer above has half as many nodes + numNodesInLayer /= 2; + } + //the first node in the layer is the root + return layer[0]; + } +} diff --git a/mainnet-contracts/src/interface/libraries/OperatorSetLib.sol b/mainnet-contracts/src/interface/libraries/OperatorSetLib.sol new file mode 100644 index 00000000..a087a7fd --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/OperatorSetLib.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +struct OperatorSet { + address avs; + uint32 id; +} + +library OperatorSetLib { + function key(OperatorSet memory os) internal pure returns (bytes32) { + return bytes32(abi.encodePacked(os.avs, uint96(os.id))); + } + + function decode(bytes32 _key) internal pure returns (OperatorSet memory) { + /// forgefmt: disable-next-item + return OperatorSet({ + avs: address(uint160(uint256(_key) >> 96)), + id: uint32(uint256(_key) & type(uint96).max) + }); + } +} diff --git a/mainnet-contracts/src/interface/libraries/SlashingLib.sol b/mainnet-contracts/src/interface/libraries/SlashingLib.sol new file mode 100644 index 00000000..cdb6ce06 --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/SlashingLib.sol @@ -0,0 +1,170 @@ +// // SPDX-License-Identifier: BUSL-1.1 +// pragma solidity ^0.8.27; + +// import "@openzeppelin/contracts/utils/math/Math.sol"; +// import "@openzeppelin-upgrades/contracts/utils/math/SafeCastUpgradeable.sol"; + +// /// @dev the stakerScalingFactor and operatorMagnitude have initial default values to 1e18 as "1" +// /// to preserve precision with uint256 math. We use `WAD` where these variables are used +// /// and divide to represent as 1 +// uint64 constant WAD = 1e18; + +// /* +// * There are 2 types of shares: +// * 1. depositShares +// * - These can be converted to an amount of tokens given a strategy +// * - by calling `sharesToUnderlying` on the strategy address (they're already tokens +// * in the case of EigenPods) +// * - These live in the storage of EPM and SM strategies +// * 2. shares +// * - For a staker, this is the amount of shares that they can withdraw +// * - For an operator, this is the sum of its staker's withdrawable shares +// * +// * Note that `withdrawal.scaledShares` is scaled for the beaconChainETHStrategy to divide by the beaconChainScalingFactor upon queueing +// * and multiply by the beaconChainScalingFactor upon withdrawal +// */ +// struct DepositScalingFactor { +// uint256 _scalingFactor; +// } + +// using SlashingLib for DepositScalingFactor global; + +// // TODO: validate order of operations everywhere +// library SlashingLib { +// using Math for uint256; +// using SlashingLib for uint256; +// using SafeCastUpgradeable for uint256; + +// // WAD MATH + +// function mulWad(uint256 x, uint256 y) internal pure returns (uint256) { +// return x.mulDiv(y, WAD); +// } + +// function divWad(uint256 x, uint256 y) internal pure returns (uint256) { +// return x.mulDiv(WAD, y); +// } + +// /** +// * @notice Used explicitly for calculating slashed magnitude, we want to ensure even in the +// * situation where an operator is slashed several times and precision has been lost over time, +// * an incoming slashing request isn't rounded down to 0 and an operator is able to avoid slashing penalties. +// */ +// function mulWadRoundUp(uint256 x, uint256 y) internal pure returns (uint256) { +// return x.mulDiv(y, WAD, Math.Rounding.Up); +// } + +// /** +// * @notice Used as part of calculating wadSlashed in the EPM to ensure that we don't overslash +// */ +// function divWadRoundUp(uint256 x, uint256 y) internal pure returns (uint256) { +// return x.mulDiv(WAD, y, Math.Rounding.Up); +// } + +// // GETTERS + +// function scalingFactor( +// DepositScalingFactor memory dsf +// ) internal pure returns (uint256) { +// return dsf._scalingFactor == 0 ? WAD : dsf._scalingFactor; +// } + +// function scaleForQueueWithdrawal( +// uint256 sharesToWithdraw, +// uint256 slashingFactor +// ) internal pure returns (uint256) { +// if (slashingFactor == 0) { +// return 0; +// } + +// return sharesToWithdraw.divWad(slashingFactor); +// } + +// function scaleForCompleteWithdrawal(uint256 scaledShares, uint256 slashingFactor) internal pure returns (uint256) { +// return scaledShares.mulWad(slashingFactor); +// } + +// /** +// * @notice Scales shares according to the difference in an operator's magnitude before and +// * after being slashed. This is used to calculate the number of slashable shares in the +// * withdrawal queue. +// * NOTE: max magnitude is guaranteed to only ever decrease. +// */ +// function scaleForBurning( +// uint256 scaledShares, +// uint64 prevMaxMagnitude, +// uint64 newMaxMagnitude +// ) internal pure returns (uint256) { +// return scaledShares.mulWad(prevMaxMagnitude - newMaxMagnitude); +// } + +// function update( +// DepositScalingFactor storage dsf, +// uint256 prevDepositShares, +// uint256 addedShares, +// uint256 slashingFactor +// ) internal { +// // If this is the staker's first deposit, set the scaling factor to +// // the inverse of slashingFactor +// if (prevDepositShares == 0) { +// dsf._scalingFactor = uint256(WAD).divWad(slashingFactor); +// return; +// } + +// /** +// * Base Equations: +// * (1) newShares = currentShares + addedShares +// * (2) newDepositShares = prevDepositShares + addedShares +// * (3) newShares = newDepositShares * newDepositScalingFactor * slashingFactor +// * +// * Plugging (1) into (3): +// * (4) newDepositShares * newDepositScalingFactor * slashingFactor = currentShares + addedShares +// * +// * Solving for newDepositScalingFactor +// * (5) newDepositScalingFactor = (currentShares + addedShares) / (newDepositShares * slashingFactor) +// * +// * Plugging in (2) into (5): +// * (7) newDepositScalingFactor = (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor) +// * Note that magnitudes must be divided by WAD for precision. Thus, +// * +// * (8) newDepositScalingFactor = WAD * (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor / WAD) +// * (9) newDepositScalingFactor = (currentShares + addedShares) * WAD / (prevDepositShares + addedShares) * WAD / slashingFactor +// */ + +// // Step 1: Calculate Numerator +// uint256 currentShares = dsf.calcWithdrawable(prevDepositShares, slashingFactor); + +// // Step 2: Compute currentShares + addedShares +// uint256 newShares = currentShares + addedShares; + +// // Step 3: Calculate newDepositScalingFactor +// /// forgefmt: disable-next-item +// uint256 newDepositScalingFactor = newShares +// .divWad(prevDepositShares + addedShares) +// .divWad(slashingFactor); + +// dsf._scalingFactor = newDepositScalingFactor; +// } + +// // CONVERSION + +// function calcWithdrawable( +// DepositScalingFactor memory dsf, +// uint256 depositShares, +// uint256 slashingFactor +// ) internal pure returns (uint256) { +// /// forgefmt: disable-next-item +// return depositShares +// .mulWad(dsf.scalingFactor()) +// .mulWad(slashingFactor); +// } + +// function calcSlashedAmount( +// uint256 operatorShares, +// uint256 prevMaxMagnitude, +// uint256 newMaxMagnitude +// ) internal pure returns (uint256) { +// // round up mulDiv so we don't overslash +// return operatorShares - operatorShares.mulDiv(newMaxMagnitude, prevMaxMagnitude, Math.Rounding.Up); +// } +// } diff --git a/mainnet-contracts/src/interface/libraries/Snapshots.sol b/mainnet-contracts/src/interface/libraries/Snapshots.sol new file mode 100644 index 00000000..6eb27727 --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/Snapshots.sol @@ -0,0 +1,182 @@ +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.0; + +// import "@openzeppelin/contracts/utils/math/Math.sol"; + +// /** +// * @title Library for handling snapshots as part of allocating and slashing. +// * @notice This library is using OpenZeppelin's CheckpointsUpgradeable library (v4.9.0) +// * and removes structs and functions that are unessential. +// * Interfaces and structs are renamed for clarity and usage. +// * Some additional functions have also been added for convenience. +// * @dev This library defines the `DefaultWadHistory` and `DefaultZeroHistory` struct, for snapshotting values as they change at different points in +// * time, and later looking up past values by block number. See {Votes} as an example. +// * +// * To create a history of snapshots define a variable type `Snapshots.DefaultWadHistory` or `Snapshots.DefaultZeroHistory` in your contract, +// * and store a new snapshot for the current transaction block using the {push} function. If there is no history yet, the value is either WAD or 0, +// * depending on the type of History struct used. This is implemented because for the AllocationManager we want the +// * the default value to be WAD(1e18) but when used in the DelegationManager we want the default value to be 0. +// * +// * _Available since v4.5._ +// */ +// library Snapshots { +// struct DefaultWadHistory { +// Snapshot[] _snapshots; +// } + +// struct DefaultZeroHistory { +// Snapshot[] _snapshots; +// } + +// struct Snapshot { +// uint32 _key; +// uint224 _value; +// } + +// error InvalidSnapshotOrdering(); + +// /** +// * @dev Pushes a (`key`, `value`) pair into a DefaultWadHistory so that it is stored as the snapshot. +// */ +// function push(DefaultWadHistory storage self, uint32 key, uint64 value) internal { +// _insert(self._snapshots, key, value); +// } + +// /** +// * @dev Pushes a (`key`, `value`) pair into a DefaultZeroHistory so that it is stored as the snapshot. +// * `value` is cast to uint224. Responsibility for the safety of this operation falls outside of this library. +// */ +// function push(DefaultZeroHistory storage self, uint32 key, uint256 value) internal { +// _insert(self._snapshots, key, uint224(value)); +// } + +// /** +// * @dev Return default value of WAD if there are no snapshots for DefaultWadHistory. +// * This is used for looking up maxMagnitudes in the AllocationManager. +// */ +// function upperLookup(DefaultWadHistory storage self, uint32 key) internal view returns (uint64) { +// return uint64(_upperLookup(self._snapshots, key, WAD)); +// } + +// /** +// * @dev Return default value of 0 if there are no snapshots for DefaultZeroHistory. +// * This is used for looking up cumulative scaled shares in the DelegationManager. +// */ +// function upperLookup(DefaultZeroHistory storage self, uint32 key) internal view returns (uint256) { +// return _upperLookup(self._snapshots, key, 0); +// } + +// /** +// * @dev Returns the value in the most recent snapshot, or WAD if there are no snapshots. +// */ +// function latest( +// DefaultWadHistory storage self +// ) internal view returns (uint64) { +// return uint64(_latest(self._snapshots, WAD)); +// } + +// /** +// * @dev Returns the value in the most recent snapshot, or 0 if there are no snapshots. +// */ +// function latest( +// DefaultZeroHistory storage self +// ) internal view returns (uint256) { +// return uint256(_latest(self._snapshots, 0)); +// } + +// /** +// * @dev Returns the number of snapshots. +// */ +// function length( +// DefaultWadHistory storage self +// ) internal view returns (uint256) { +// return self._snapshots.length; +// } + +// /** +// * @dev Returns the number of snapshots. +// */ +// function length( +// DefaultZeroHistory storage self +// ) internal view returns (uint256) { +// return self._snapshots.length; +// } + +// /** +// * @dev Pushes a (`key`, `value`) pair into an ordered list of snapshots, either by inserting a new snapshot, +// * or by updating the last one. +// */ +// function _insert(Snapshot[] storage self, uint32 key, uint224 value) private { +// uint256 pos = self.length; + +// if (pos > 0) { +// // Validate that inserted keys are always >= the previous key +// Snapshot memory last = _unsafeAccess(self, pos - 1); +// require(last._key <= key, InvalidSnapshotOrdering()); + +// // Update existing snapshot if `key` matches +// if (last._key == key) { +// _unsafeAccess(self, pos - 1)._value = value; +// return; +// } +// } + +// // `key` was not in the list; push as a new entry +// self.push(Snapshot({_key: key, _value: value})); +// } + +// /** +// * @dev Returns the value in the last (most recent) snapshot with key lower or equal than the search key, or `defaultValue` if there is none. +// */ +// function _upperLookup( +// Snapshot[] storage snapshots, +// uint32 key, +// uint224 defaultValue +// ) private view returns (uint224) { +// uint256 len = snapshots.length; +// uint256 pos = _upperBinaryLookup(snapshots, key, 0, len); +// return pos == 0 ? defaultValue : _unsafeAccess(snapshots, pos - 1)._value; +// } + +// /** +// * @dev Returns the value in the most recent snapshot, or `defaultValue` if there are no snapshots. +// */ +// function _latest(Snapshot[] storage snapshots, uint224 defaultValue) private view returns (uint224) { +// uint256 pos = snapshots.length; +// return pos == 0 ? defaultValue : _unsafeAccess(snapshots, pos - 1)._value; +// } + +// /** +// * @dev Return the index of the last (most recent) snapshot with key lower or equal than the search key, or `high` if there is none. +// * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. +// * +// * WARNING: `high` should not be greater than the array's length. +// */ +// function _upperBinaryLookup( +// Snapshot[] storage self, +// uint32 key, +// uint256 low, +// uint256 high +// ) private view returns (uint256) { +// while (low < high) { +// uint256 mid = MathUpgradeable.average(low, high); +// if (_unsafeAccess(self, mid)._key > key) { +// high = mid; +// } else { +// low = mid + 1; +// } +// } +// return high; +// } + +// /** +// * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. +// */ +// function _unsafeAccess(Snapshot[] storage self, uint256 pos) private pure returns (Snapshot storage result) { +// assembly { +// mstore(0, self.slot) +// result.slot := add(keccak256(0, 0x20), pos) +// } +// } +// } diff --git a/mainnet-contracts/src/struct/ModuleStorage.sol b/mainnet-contracts/src/struct/ModuleStorage.sol index 807ee0e7..319c572f 100644 --- a/mainnet-contracts/src/struct/ModuleStorage.sol +++ b/mainnet-contracts/src/struct/ModuleStorage.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IEigenPod } from "eigenlayer/interfaces/IEigenPod.sol"; +import { IEigenPod } from "../interface/EigenLayer-Slashing/IEigenPod.sol"; /** * @custom:storage-location erc7201:PufferModule.storage diff --git a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol index 9a1afd3d..f88ba029 100644 --- a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol @@ -12,10 +12,10 @@ import { PufferVaultV4 } from "../../src/PufferVaultV4.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../../src/interface/EigenLayer/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer/IEigenLayer.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer/IDelegationManager.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; struct AssetValue { IERC20 asset; diff --git a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol index 5354d985..a0d26c7c 100644 --- a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol +++ b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol @@ -9,7 +9,7 @@ import { PufferVaultV2Tests } from "../../test/mocks/PufferVaultV2Tests.sol"; import { PufferDepositorV2 } from "../../src/PufferDepositorV2.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { MockPufferOracle } from "../mocks/MockPufferOracle.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer/IEigenLayer.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IPufferVault } from "../../src/interface/IPufferVault.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -22,13 +22,13 @@ import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessMana import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { IWstETH } from "../../src/interface/Lido/IWstETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "../../src/interface/EigenLayer/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { Timelock } from "../../src/Timelock.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; import { Permit } from "../../src/structs/Permit.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer/IDelegationManager.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; /** * @dev PufferDepositor and PufferVault tests (v1) diff --git a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol index e65cf1f8..ef60f431 100644 --- a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol @@ -6,12 +6,11 @@ import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; -import { IStrategy } from "../../src/interface/EigenLayer/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer/IEigenLayer.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer/IDelegationManager.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import { PufferDeployment } from "../../src/structs/PufferDeployment.sol"; diff --git a/mainnet-contracts/test/MainnetForkTestHelper.sol b/mainnet-contracts/test/MainnetForkTestHelper.sol index c0fd9b74..5ea810d6 100644 --- a/mainnet-contracts/test/MainnetForkTestHelper.sol +++ b/mainnet-contracts/test/MainnetForkTestHelper.sol @@ -10,7 +10,7 @@ import { PufferVaultV2Tests } from "../test/mocks/PufferVaultV2Tests.sol"; import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; import { PufferDepositorV2 } from "../src/PufferDepositorV2.sol"; import { MockPufferOracle } from "./mocks/MockPufferOracle.sol"; -import { IEigenLayer } from "../src/interface/EigenLayer/IEigenLayer.sol"; +import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol"; @@ -19,13 +19,13 @@ import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { IPufferOracle } from "../src/interface/IPufferOracle.sol"; import { IWstETH } from "../src/interface/Lido/IWstETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../src/interface/EigenLayer/IStrategy.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { Timelock } from "../src/Timelock.sol"; import { IWETH } from "../src/interface/Other/IWETH.sol"; import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; import { Permit } from "../src/structs/Permit.sol"; import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer/IDelegationManager.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { DeployerHelper } from "../script/DeployerHelper.s.sol"; import { IPufferRevenueDepositor } from "../src/interface/IPufferRevenueDepositor.sol"; diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index f448aea0..f36823db 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -5,16 +5,15 @@ import "forge-std/console.sol"; import { IntegrationTestHelper } from "../helpers/IntegrationTestHelper.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; -import { IRestakingOperator } from "../../src/interface/IRestakingOperator.sol"; import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.sol"; import { RestakingOperator } from "../../src/RestakingOperator.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; -import { IStrategyManager } from "eigenlayer/interfaces/IStrategyManager.sol"; -import { IStrategy } from "eigenlayer/interfaces/IStrategy.sol"; +import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategyManager } from "../../src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IBLSApkRegistry, IRegistryCoordinator } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { IAVSDirectory } from "eigenlayer/interfaces/IAVSDirectory.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; +import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; import { IRegistryCoordinatorExtended } from "../../src/interface/IRegistryCoordinatorExtended.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; @@ -47,7 +46,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { function test_opt_into_slashing() public { vm.startPrank(DAO); - IRestakingOperator operator = _createRestakingOperator(); + RestakingOperator operator = _createRestakingOperator(); address slasher = address(1235); @@ -58,7 +57,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { function test_modify_operator() public { vm.startPrank(DAO); - IRestakingOperator operator = _createRestakingOperator(); + RestakingOperator operator = _createRestakingOperator(); IDelegationManager.OperatorDetails memory newOperatorDetails = IDelegationManager.OperatorDetails({ __deprecated_earningsReceiver: address(this), @@ -79,7 +78,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { function test_update_metadata_uri() public { vm.startPrank(DAO); - IRestakingOperator operator = _createRestakingOperator(); + RestakingOperator operator = _createRestakingOperator(); string memory newUri = "https://puffer.fi/updated.json"; @@ -95,7 +94,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { // // vm.startPrank(DAO); // // https://holesky.eigenlayer.xyz/operator/0xe2c2dc296a0bff351f6bc3e98d37ea798e393e56 // address restakingOperator = 0xe2c2dc296a0bFF351F6bC3e98D37ea798e393e56; - // // IRestakingOperator restakingOperator = _createRestakingOperator(); + // // RestakingOperator restakingOperator = _createRestakingOperator(); // // _depositToWETHEigenLayerStrategyAndDelegateTo(address(restakingOperator)); @@ -124,7 +123,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { // bytes memory hashCall = abi.encodeCall( // IPufferModuleManager.updateAVSRegistrationSignatureProof, - // (IRestakingOperator(restakingOperator), digestHash, operatorAddress) + // (RestakingOperator(restakingOperator), digestHash, operatorAddress) // ); // //@todo has to be updated manually on the contract @@ -133,7 +132,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { // console.logBytes(hashCall); // // pufferModuleManager.updateAVSRegistrationSignatureProof( - // // IRestakingOperator(restakingOperator), digestHash, operatorAddress + // // RestakingOperator(restakingOperator), digestHash, operatorAddress // // ); // IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = @@ -151,7 +150,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { // bytes memory calldataToRegister = abi.encodeCall( // IPufferModuleManager.callRegisterOperatorToAVSWithChurn, // ( - // IRestakingOperator(restakingOperator), + // RestakingOperator(restakingOperator), // EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY, // bytes(hex"01"), // "20.64.16.29:32005;32004", @@ -171,7 +170,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { // 4. Dao registers the operator by submitting his signature to the AVS // pufferModuleManager.callRegisterOperatorToAVSWithChurn({ - // restakingOperator: IRestakingOperator(restakingOperator), + // restakingOperator: RestakingOperator(restakingOperator), // avsRegistryCoordinator: EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY, // quorumNumbers: bytes(hex"01"), // socket: "103.199.107.52:32005;32004", @@ -204,8 +203,8 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { // Creates a new restaking operator and returns it // metadataURI is used as seed for create2 in EL - function _createRestakingOperator() internal returns (IRestakingOperator) { - IRestakingOperator operator = moduleManager.createNewRestakingOperator({ + function _createRestakingOperator() internal returns (RestakingOperator) { + RestakingOperator operator = moduleManager.createNewRestakingOperator({ metadataURI: "https://puffer.fi/metadata.json", delegationApprover: address(0), stakerOptOutWindowBlocks: 0 diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol index f8a7b491..fb42037b 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol @@ -7,20 +7,17 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils import { DeployEverything } from "script/DeployEverything.s.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { PufferModule } from "../../src/PufferModule.sol"; -import { IRestakingOperator } from "../../src/interface/IRestakingOperator.sol"; import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.sol"; import { AVSContractsRegistry } from "../../src/AVSContractsRegistry.sol"; import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; -import { IStrategy } from "eigenlayer/interfaces/IStrategy.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { IAVSDirectory } from "eigenlayer/interfaces/IAVSDirectory.sol"; -import { IRewardsCoordinator } from "../../src/interface/EigenLayer/IRewardsCoordinator.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; +import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; +import { IRewardsCoordinator } from "../../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; -import { IRegistryCoordinatorExtended } from "../../src/interface/IRegistryCoordinatorExtended.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; diff --git a/mainnet-contracts/test/fork-tests/VerifyWithdrawalCredentials.integration.t.sol b/mainnet-contracts/test/fork-tests/VerifyWithdrawalCredentials.integration.t.sol deleted file mode 100644 index 2cd98a36..00000000 --- a/mainnet-contracts/test/fork-tests/VerifyWithdrawalCredentials.integration.t.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { console } from "forge-std/console.sol"; -import { ProofParsing } from "../helpers/ProofParsing.sol"; -import { DeployEverything } from "script/DeployEverything.s.sol"; -import { DeployEverything } from "script/DeployEverything.s.sol"; -import { IEigenPod } from "eigenlayer/interfaces/IEigenPod.sol"; -import { BeaconChainProofs } from "eigenlayer/libraries/BeaconChainProofs.sol"; -import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; - -interface IElOracle { - function addTimestamp(uint256 timestamp) external; - function timestampToBlockRoot(uint256 timestamp) external view returns (bytes32); -} - -contract PufferModuleManagerIntegrationTest is ProofParsing { - IElOracle elOracle = IElOracle(0x4C116BB629bff7A8373c2378bBd919f8349B8f25); - address pufferModuleManager = 0xe4695ab93163F91665Ce5b96527408336f070a71; - - function setUp() public { - // We create fork on 1269510 block, which has timestamp of 1712102016 (Tuesday, 2 April 2024 23:53:36) - vm.createSelectFork(vm.rpcUrl("holesky"), 1269510); - } - - // Helper Functions - function _getStateRootProof() internal returns (BeaconChainProofs.StateRootProof memory) { - return BeaconChainProofs.StateRootProof(getBeaconStateRoot(), abi.encodePacked(getStateRootProof())); - } -} diff --git a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol index 9a183ff2..e1255dbf 100644 --- a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol +++ b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol @@ -4,18 +4,18 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/console.sol"; import { Test } from "forge-std/Test.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { IRestakingOperator } from "src/interface/IRestakingOperator.sol"; import { IPufferModuleManager } from "src/interface/IPufferModuleManager.sol"; import { PufferModuleManager } from "src/PufferModuleManager.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { IAVSDirectory } from "eigenlayer/interfaces/IAVSDirectory.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; +import { IAVSDirectory } from "src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; import { IRegistryCoordinatorExtended } from "src/interface/IRegistryCoordinatorExtended.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { RestakingOperator } from "src/RestakingOperator.sol"; interface Weth { function deposit() external payable; @@ -84,7 +84,7 @@ contract PufferModuleManagerHoleskyTestnetFFI is Test { bytes memory hashCall = abi.encodeCall( IPufferModuleManager.updateAVSRegistrationSignatureProof, - (IRestakingOperator(RESTAKING_OPERATOR_CONTRACT), digestHash, operatorAddress) + (RestakingOperator(RESTAKING_OPERATOR_CONTRACT), digestHash, operatorAddress) ); vm.startPrank(PUFFER_SHARED_DEV_WALLET); // 'DAO' role on the Holesky testnet @@ -100,7 +100,7 @@ contract PufferModuleManagerHoleskyTestnetFFI is Test { bytes memory calldataToRegister = abi.encodeCall( IPufferModuleManager.callRegisterOperatorToAVS, ( - IRestakingOperator(RESTAKING_OPERATOR_CONTRACT), + RestakingOperator(RESTAKING_OPERATOR_CONTRACT), EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY, bytes(hex"01"), "20.64.16.29:32005;32004", // Update to the correct value diff --git a/mainnet-contracts/test/mocks/DelegationManagerMock.sol b/mainnet-contracts/test/mocks/DelegationManagerMock.sol index 8776bbae..b90da079 100644 --- a/mainnet-contracts/test/mocks/DelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/DelegationManagerMock.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { IStrategy } from "eigenlayer/interfaces/IStrategy.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; -import { IStrategyManager } from "eigenlayer/interfaces/IStrategyManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategyManager } from "src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract DelegationManagerMock { diff --git a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol index 63293b1c..4d3f8e6f 100644 --- a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol @@ -1,60 +1,323 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IEigenLayer } from "../../src/interface/EigenLayer/IEigenLayer.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer/IDelegationManager.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; contract EigenLayerDelegationManagerMock is IDelegationManager { /** - * Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed - * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from - * their operator. + * @dev Initializes the initial owner and paused status. + */ + function initialize(address initialOwner, uint256 initialPausedStatus) external { } + + /** + * @notice Registers the caller as an operator in EigenLayer. + * @param initDelegationApprover is an address that, if set, must provide a signature when stakers delegate + * to an operator. + * @param allocationDelay The delay before allocations take effect. + * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator. + * + * @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself". + * @dev This function will revert if the caller is already delegated to an operator. + * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event + */ + function registerAsOperator(address initDelegationApprover, uint32 allocationDelay, string calldata metadataURI) + external + { } + + /** + * @notice Updates an operator's stored `delegationApprover`. + * @param operator is the operator to update the delegationApprover for + * @param newDelegationApprover is the new delegationApprover for the operator + * + * @dev The caller must have previously registered as an operator in EigenLayer. + */ + function modifyOperatorDetails(address operator, address newDelegationApprover) external { } + + /** + * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated. + * @param operator The operator to update metadata for + * @param metadataURI The URI for metadata associated with an operator + * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event + */ + function updateOperatorMetadataURI(address operator, string calldata metadataURI) external { } + + /** + * @notice Caller delegates their stake to an operator. + * @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer. + * @param approverSignatureAndExpiry Verifies the operator approves of this delegation + * @param approverSalt A unique single use value tied to an individual signature. + * @dev The approverSignatureAndExpiry is used in the event that the operator's `delegationApprover` address is set to a non-zero value. + * @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input + * in this case to save on complexity + gas costs + * @dev If the staker delegating has shares in a strategy that the operator was slashed 100% for (the operator's maxMagnitude = 0), + * then delegation is blocked and will revert. + */ + function delegateTo(address operator, SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt) + external + { } + + /** + * @notice Undelegates the staker from the operator who they are delegated to. + * Queues withdrawals of all of the staker's withdrawable shares in the StrategyManager (to the staker) and/or EigenPodManager, if necessary. + * @param staker The account to be undelegated. + * @return withdrawalRoots The roots of the newly queued withdrawals, if a withdrawal was queued. Otherwise just bytes32(0). * - * All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay. + * @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves. + * @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover" + * @dev Reverts if the `staker` is already undelegated. */ - function queueWithdrawals(QueuedWithdrawalParams[] calldata queuedWithdrawalParams) + function undelegate(address staker) external returns (bytes32[] memory withdrawalRoots) { } + + /** + * @notice Undelegates the staker from their current operator, and redelegates to `newOperator` + * Queues a withdrawal for all of the staker's withdrawable shares. These shares will only be + * delegated to `newOperator` AFTER the withdrawal is completed. + * @dev This method acts like a call to `undelegate`, then `delegateTo` + * @param newOperator the new operator that will be delegated all assets + * @dev NOTE: the following 2 params are ONLY checked if `newOperator` has a `delegationApprover`. + * If not, they can be left empty. + * @param newOperatorApproverSig A signature from the operator's `delegationApprover` + * @param approverSalt A unique single use value tied to the approver's signature + */ + function redelegate(address newOperator, SignatureWithExpiry memory newOperatorApproverSig, bytes32 approverSalt) external - returns (bytes32[] memory) + returns (bytes32[] memory withdrawalRoots) { } /** - * @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer` - * @param withdrawal The Withdrawal to complete. + * @notice Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed + * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from + * their operator. + * + * All withdrawn shares/strategies are placed in a queue and can be withdrawn after a delay. Withdrawals + * are still subject to slashing during the delay period so the amount withdrawn on completion may actually be less + * than what was queued if slashing has occurred in that period. + * + * @dev To view what the staker is able to queue withdraw, see `getWithdrawableShares()` + */ + function queueWithdrawals(QueuedWithdrawalParams[] calldata params) external returns (bytes32[] memory) { } + + /** + * @notice Used to complete the all queued withdrawals. + * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` + * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. + * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. + * @param numToComplete The number of withdrawals to complete. This must be less than or equal to the number of queued withdrawals. + * @dev See `completeQueuedWithdrawal` for relevant dev tags + */ + function completeQueuedWithdrawals( + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens, + uint256 numToComplete + ) external { } + + /** + * @notice Used to complete the lastest queued withdrawal. + * @param withdrawal The withdrawal to complete. * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array. - * This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused) - * @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array - * @param receiveAsTokens If true, the shares specified in the withdrawal will be withdrawn from the specified strategies themselves + * @param receiveAsTokens If true, the shares calculated to be withdrawn will be withdrawn from the specified strategies themselves * and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies * will simply be transferred to the caller directly. - * @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw` * @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that * any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in * any other strategies, which will be transferred to the withdrawer. */ - function completeQueuedWithdrawal( - Withdrawal calldata withdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex, - bool receiveAsTokens - ) external { } + function completeQueuedWithdrawal(Withdrawal calldata withdrawal, IERC20[] calldata tokens, bool receiveAsTokens) + external + { } /** - * @notice Array-ified version of `completeQueuedWithdrawal`. + * @notice Used to complete the all queued withdrawals. * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` - * @param withdrawals The Withdrawals to complete. + * @param withdrawals Array of Withdrawals to complete. See `completeQueuedWithdrawal` for the usage of a single Withdrawal. * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. - * @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index. * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. * @dev See `completeQueuedWithdrawal` for relevant dev tags */ function completeQueuedWithdrawals( Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens ) external { } + /** + * @notice Increases a staker's delegated share balance in a strategy. Note that before adding to operator shares, + * the delegated delegatedShares. The staker's depositScalingFactor is updated here. + * @param staker The address to increase the delegated shares for their operator. + * @param strategy The strategy in which to increase the delegated shares. + * @param prevDepositShares The number of deposit shares the staker already had in the strategy. This is the shares amount stored in the + * StrategyManager/EigenPodManager for the staker's shares. + * @param addedShares The number of shares added to the staker's shares in the strategy + * + * @dev *If the staker is actively delegated*, then increases the `staker`'s delegated delegatedShares in `strategy`. + * Otherwise does nothing. + * @dev If the operator was slashed 100% for the strategy (the operator's maxMagnitude = 0), then increasing delegated shares is blocked and will revert. + * @dev Callable only by the StrategyManager or EigenPodManager. + */ + function increaseDelegatedShares(address staker, IStrategy strategy, uint256 prevDepositShares, uint256 addedShares) + external + { } + + /** + * @notice If the staker is delegated, decreases its operator's shares in response to + * a decrease in balance in the beaconChainETHStrategy + * @param staker the staker whose operator's balance will be decreased + * @param curDepositShares the current deposit shares held by the staker + * @param beaconChainSlashingFactorDecrease the amount that the staker's beaconChainSlashingFactor has decreased by + * @dev Note: `beaconChainSlashingFactorDecrease` are assumed to ALWAYS be < 1 WAD. + * These invariants are maintained in the EigenPodManager. + */ + function decreaseDelegatedShares(address staker, uint256 curDepositShares, uint64 beaconChainSlashingFactorDecrease) + external + { } + + /** + * @notice Decreases the operators shares in storage after a slash and burns the corresponding Strategy shares + * by calling into the StrategyManager or EigenPodManager to burn the shares. + * @param operator The operator to decrease shares for + * @param strategy The strategy to decrease shares for + * @param prevMaxMagnitude the previous maxMagnitude of the operator + * @param newMaxMagnitude the new maxMagnitude of the operator + * @dev Callable only by the AllocationManager + * @dev Note: Assumes `prevMaxMagnitude <= newMaxMagnitude`. This invariant is maintained in + * the AllocationManager. + */ + function burnOperatorShares(address operator, IStrategy strategy, uint64 prevMaxMagnitude, uint64 newMaxMagnitude) + external + { } + + /** + * + * VIEW FUNCTIONS + * + */ + + /** + * @notice returns the address of the operator that `staker` is delegated to. + * @notice Mapping: staker => operator whom the staker is currently delegated to. + * @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator. + */ + function delegatedTo(address staker) external view returns (address) { } + + /** + * @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover. + * @dev Salts are used in the `delegateTo` function. Note that this function only processes the delegationApprover's + * signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`. + */ + function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool) { } + + /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated. + /// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes. + function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) { } + + /** + * @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise. + */ + function isDelegated(address staker) external view returns (bool) { } + + /** + * @notice Returns true is an operator has previously registered for delegation. + */ + function isOperator(address operator) external view returns (bool) { } + + /** + * @notice Returns the delegationApprover account for an operator + */ + function delegationApprover(address operator) external view returns (address) { } + + /** + * @notice Returns the shares that an operator has delegated to them in a set of strategies + * @param operator the operator to get shares for + * @param strategies the strategies to get shares for + */ + function getOperatorShares(address operator, IStrategy[] memory strategies) + external + view + returns (uint256[] memory) + { } + + /** + * @notice Returns the shares that a set of operators have delegated to them in a set of strategies + * @param operators the operators to get shares for + * @param strategies the strategies to get shares for + */ + function getOperatorsShares(address[] memory operators, IStrategy[] memory strategies) + external + view + returns (uint256[][] memory) + { } + + /** + * @notice Returns amount of withdrawable shares from an operator for a strategy that is still in the queue + * and therefore slashable. Note that the *actual* slashable amount could be less than this value as this doesn't account + * for amounts that have already been slashed. This assumes that none of the shares have been slashed. + * @param operator the operator to get shares for + * @param strategy the strategy to get shares for + * @return the amount of shares that are slashable in the withdrawal queue for an operator and a strategy + */ + function getSlashableSharesInQueue(address operator, IStrategy strategy) external view returns (uint256) { } + + /** + * @notice Given a staker and a set of strategies, return the shares they can queue for withdrawal and the + * corresponding depositShares. + * This value depends on which operator the staker is delegated to. + * The shares amount returned is the actual amount of Strategy shares the staker would receive (subject + * to each strategy's underlying shares to token ratio). + */ + function getWithdrawableShares(address staker, IStrategy[] memory strategies) + external + view + returns (uint256[] memory withdrawableShares, uint256[] memory depositShares) + { } + + /** + * @notice Returns the number of shares in storage for a staker and all their strategies + */ + function getDepositedShares(address staker) external view returns (IStrategy[] memory, uint256[] memory) { } + + /** + * @notice Returns the scaling factor applied to a staker's deposits for a given strategy + */ + function depositScalingFactor(address staker, IStrategy strategy) external view returns (uint256) { } + + /** + * @notice Returns the minimum withdrawal delay in blocks to pass for withdrawals queued to be completable. + * Also applies to legacy withdrawals so any withdrawals not completed prior to the slashing upgrade will be subject + * to this longer delay. + */ + function MIN_WITHDRAWAL_DELAY_BLOCKS() external view returns (uint32) { } + + /// @notice Returns a list of pending queued withdrawals for a `staker`, and the `shares` to be withdrawn. + function getQueuedWithdrawals(address staker) + external + view + returns (Withdrawal[] memory withdrawals, uint256[][] memory shares) + { } + /// @notice Returns the keccak256 hash of `withdrawal`. function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) { } + + /** + * @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` function. + * @param staker The account delegating their stake + * @param operator The account receiving delegated stake + * @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general) + * @param approverSalt A unique and single use value associated with the approver signature. + * @param expiry Time after which the approver's signature becomes invalid + */ + function calculateDelegationApprovalDigestHash( + address staker, + address operator, + address _delegationApprover, + bytes32 approverSalt, + uint256 expiry + ) external view returns (bytes32) { } + + /// @notice return address of the beaconChainETHStrategy + function beaconChainETHStrategy() external view returns (IStrategy) { } + + /// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract + function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) { } } diff --git a/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol index 5e56ad41..0faf9f7c 100644 --- a/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IEigenLayer } from "../../src/interface/EigenLayer/IEigenLayer.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IStrategy } from "../../src/interface/EigenLayer/IStrategy.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; contract EigenLayerManagerMock is IEigenLayer { function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares) { } diff --git a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol index dc345288..580f8d52 100644 --- a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol @@ -2,125 +2,126 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Test.sol"; -import "eigenlayer/interfaces/IEigenPodManager.sol"; -import "eigenlayer-test/mocks/EigenPodMock.sol"; +import "src/interface/Eigenlayer-Slashing/IEigenPodManager.sol"; +import "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; -contract EigenPodManagerMock is IEigenPodManager, Test { - function slasher() external pure returns (ISlasher) { } - - function createPod() external returns (address) { - return address(new EigenPodMock()); - } - - function addShares(address, uint256) external pure returns (uint256) { - return 55; - } - - function beaconChainETHStrategy() external pure returns (IStrategy) { } +contract EigenPodMock { } - function eigenPodBeacon() external pure returns (IBeacon) { - return IBeacon(address(99)); - } - - function ethPOS() external pure returns (IETHPOSDeposit) { - return IETHPOSDeposit(address(99)); - } - - function getBlockRootAtTimestamp(uint64) external pure returns (bytes32) { - return bytes32("asdf"); - } - - function maxPods() external pure returns (uint256) { - return 100; - } +contract EigenPodManagerMock is IEigenPodManager, Test { + /// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + function removeDepositShares(address staker, IStrategy strategy, uint256 depositSharesToRemove) external { } + + /// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + /// @dev token is not validated when talking to the EigenPodManager + /// @return existingDepositShares the shares the staker had before any were added + /// @return addedShares the new shares added to the staker's balance + function addShares(address staker, IStrategy strategy, IERC20 token, uint256 shares) + external + returns (uint256, uint256) + { } - function numPods() external pure returns (uint256) { - return 10; - } + /// @notice Used by the DelegationManager to convert withdrawn descaled shares to tokens and send them to a staker + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + /// @dev token is not validated when talking to the EigenPodManager + function withdrawSharesAsTokens(address staker, IStrategy strategy, IERC20 token, uint256 shares) external { } - function podOwnerShares(address) external pure returns (int256) { - return 5; - } + /// @notice Returns the current shares of `user` in `strategy` + /// @dev strategy must be beaconChainETH when talking to the EigenPodManager + /// @dev returns 0 if the user has negative shares + function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 depositShares) { } /** - * @notice the deneb hard fork timestamp used to determine which proof path to use for proving a withdrawal + * @notice Creates an EigenPod for the sender. + * @dev Function will revert if the `msg.sender` already has an EigenPod. + * @dev Returns EigenPod address */ - function denebForkTimestamp() external view returns (uint64) { } + function createPod() external returns (address) { } /** - * setting the deneb hard fork timestamp by the eigenPodManager owner - * @dev this function is designed to be called twice. Once, it is set to type(uint64).max - * prior to the actual deneb fork timestamp being set, and then the second time it is set - * to the actual deneb fork timestamp. + * @notice Stakes for a new beacon chain validator on the sender's EigenPod. + * Also creates an EigenPod for the sender if they don't have one already. + * @param pubkey The 48 bytes public key of the beacon chain validator. + * @param signature The validator's signature of the deposit data. + * @param depositDataRoot The root/hash of the deposit data for the validator's deposit. */ - function setDenebForkTimestamp(uint64 newDenebForkTimestamp) external { } + function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable { } - function recordBeaconChainETHBalanceUpdate(address podOwner, int256 sharesDelta) external { } - function removeShares(address podOwner, uint256 shares) external { } - function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external { } - - function stake(bytes calldata, /*pubkey*/ bytes calldata, /*signature*/ bytes32 /*depositDataRoot*/ ) + /** + * @notice Changes the `podOwner`'s shares by `sharesDelta` and performs a call to the DelegationManager + * to ensure that delegated shares are also tracked correctly + * @param podOwner is the pod owner whose balance is being updated. + * @param prevRestakedBalanceWei is the total amount restaked through the pod before the balance update + * @param balanceDeltaWei is the amount the balance changed + * @dev Callable only by the podOwner's EigenPod contract. + * @dev Reverts if `sharesDelta` is not a whole Gwei amount + */ + function recordBeaconChainETHBalanceUpdate(address podOwner, uint256 prevRestakedBalanceWei, int256 balanceDeltaWei) external - payable { } - function restakeBeaconChainETH(address, /*podOwner*/ uint256 /*amount*/ ) external pure { } + /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed. + function ownerToPod(address podOwner) external view returns (IEigenPod) { } - function recordBeaconChainETHBalanceUpdate( - address, /*podOwner*/ - uint256, /*beaconChainETHStrategyIndex*/ - int256 /*sharesDelta*/ - ) external pure { } + /// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not). + function getPod(address podOwner) external view returns (IEigenPod) { } - function withdrawRestakedBeaconChainETH(address, /*podOwner*/ address, /*recipient*/ uint256 /*amount*/ ) - external - pure - { } + /// @notice The ETH2 Deposit Contract + function ethPOS() external view returns (IETHPOSDeposit) { } - // function updateBeaconChainOracle(IBeaconChainOracle /*newBeaconChainOracle*/ ) external pure { } + /// @notice Beacon proxy to which the EigenPods point + function eigenPodBeacon() external view returns (IBeacon) { } - function ownerToPod(address /*podOwner*/ ) external view returns (IEigenPod) { - // return IEigenPod(address(555)); - return IEigenPod(address(uint160(uint256(uint160(msg.sender)) + 1))); - } + /// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise. + function hasPod(address podOwner) external view returns (bool) { } - function getPod(address podOwner) external pure returns (IEigenPod) { - return IEigenPod(podOwner); - } + /// @notice Returns the number of EigenPods that have been created + function numPods() external view returns (uint256) { } - function getBeaconChainStateRoot(uint64 /*blockNumber*/ ) external pure returns (bytes32) { - return bytes32(0); - } - - function strategyManager() external pure returns (IStrategyManager) { - return IStrategyManager(address(0)); - } - - function decrementWithdrawableRestakedExecutionLayerGwei(address podOwner, uint256 amountWei) external { } + /** + * @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy. + * @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can + * decrease between the pod owner queuing and completing a withdrawal. + * When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_. + * Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this + * as the withdrawal "paying off the deficit". + */ + function podOwnerDepositShares(address podOwner) external view returns (int256) { } - function incrementWithdrawableRestakedExecutionLayerGwei(address podOwner, uint256 amountWei) external { } + /// @notice returns canonical, virtual beaconChainETH strategy + function beaconChainETHStrategy() external view returns (IStrategy) { } - function hasPod(address /*podOwner*/ ) external pure returns (bool) { - return false; - } + /** + * @notice Returns the historical sum of proportional balance decreases a pod owner has experienced when + * updating their pod's balance. + */ + function beaconChainSlashingFactor(address staker) external view returns (uint64) { } - function pause(uint256 /*newPausedStatus*/ ) external { } + /** + * @notice This function is used to pause an EigenLayer contract's functionality. + * It is permissioned to the `pauser` address, which is expected to be a low threshold multisig. + * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once. + * @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0. + */ + function pause(uint256 newPausedStatus) external { } + /** + * @notice Alias for `pause(type(uint256).max)`. + */ function pauseAll() external { } - function paused() external pure returns (uint256) { - return 0; - } - - function paused(uint8 /*index*/ ) external pure returns (bool) { - return false; - } - - function setPauserRegistry(IPauserRegistry /*newPauserRegistry*/ ) external { } + /** + * @notice This function is used to unpause an EigenLayer contract's functionality. + * It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract. + * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once. + * @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1. + */ + function unpause(uint256 newPausedStatus) external { } - function pauserRegistry() external pure returns (IPauserRegistry) { - return IPauserRegistry(address(0)); - } + /// @notice Returns the current paused status as a uint256. + function paused() external view returns (uint256) { } - function unpause(uint256 /*newPausedStatus*/ ) external { } + /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise + function paused(uint8 index) external view returns (bool) { } } diff --git a/mainnet-contracts/test/mocks/PausableMock.sol b/mainnet-contracts/test/mocks/PausableMock.sol index c69d9541..d8f5b17e 100644 --- a/mainnet-contracts/test/mocks/PausableMock.sol +++ b/mainnet-contracts/test/mocks/PausableMock.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.12; -import "eigenlayer/interfaces/IPausable.sol"; +import "src/interface/Eigenlayer-Slashing/IPausable.sol"; /** * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract. @@ -111,18 +111,13 @@ contract PausableMock is IPausable { return ((_paused & mask) == mask); } - /// @notice Allows the unpauser to set a new pauser registry - function setPauserRegistry(IPauserRegistry newPauserRegistry) external onlyUnpauser { - _setPauserRegistry(newPauserRegistry); - } - /// internal function for setting pauser registry function _setPauserRegistry(IPauserRegistry newPauserRegistry) internal { require( address(newPauserRegistry) != address(0), "Pausable._setPauserRegistry: newPauserRegistry cannot be the zero address" ); - emit PauserRegistrySet(pauserRegistry, newPauserRegistry); + // emit PauserRegistrySet(pauserRegistry, newPauserRegistry); pauserRegistry = newPauserRegistry; } diff --git a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol index 3c8cf2b3..a23c9aff 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "src/PufferVaultV2.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "src/interface/EigenLayer/IStrategy.sol"; +import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/EigenLayer/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; contract PufferVaultV2Tests is PufferVaultV2 { constructor( diff --git a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol index 1f596dd0..e949f9e8 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV4 } from "src/PufferVaultV4.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/EigenLayer/IEigenLayer.sol"; -import { IStrategy } from "src/interface/EigenLayer/IStrategy.sol"; +import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/EigenLayer/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IPufferRevenueDepositor } from "src/interface/IPufferRevenueDepositor.sol"; contract PufferVaultV4Tests is PufferVaultV4 { diff --git a/mainnet-contracts/test/mocks/SlasherMock.sol b/mainnet-contracts/test/mocks/SlasherMock.sol index 4041566c..09a2b9fd 100644 --- a/mainnet-contracts/test/mocks/SlasherMock.sol +++ b/mainnet-contracts/test/mocks/SlasherMock.sol @@ -1,611 +1,615 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.8.12; - -import "eigenlayer/interfaces/ISlasher.sol"; -import "eigenlayer/interfaces/IDelegationManager.sol"; -import "eigenlayer/interfaces/IStrategyManager.sol"; -import "test/mocks/StructuredLinkedListMock.sol"; -import "test/mocks/PausableMock.sol"; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -/** - * @title The primary 'slashing' contract for EigenLayer. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice This contract specifies details on slashing. The functionalities are: - * - adding contracts who have permission to perform slashing, - * - revoking permission for slashing from specified contracts, - * - tracking historic stake updates to ensure that withdrawals can only be completed once no middlewares have slashing rights - * over the funds being withdrawn - */ -contract SlasherMock is Initializable, OwnableUpgradeable, ISlasher, PausableMock { - using StructuredLinkedListMock for StructuredLinkedListMock.List; - - uint256 private constant HEAD = 0; - - uint8 internal constant PAUSED_OPT_INTO_SLASHING = 0; - uint8 internal constant PAUSED_FIRST_STAKE_UPDATE = 1; - uint8 internal constant PAUSED_NEW_FREEZING = 2; - - /// @notice The central StrategyManager contract of EigenLayer - IStrategyManager public immutable strategyManager; - /// @notice The DelegationManager contract of EigenLayer - IDelegationManager public immutable delegation; - // operator => whitelisted contract with slashing permissions => (the time before which the contract is allowed to slash the user, block it was last updated) - mapping(address => mapping(address => MiddlewareDetails)) internal _whitelistedContractDetails; - // staker => if their funds are 'frozen' and potentially subject to slashing or not - mapping(address => bool) internal frozenStatus; - - uint32 internal constant MAX_CAN_SLASH_UNTIL = type(uint32).max; - - /** - * operator => a linked list of the addresses of the whitelisted middleware with permission to slash the operator, i.e. which - * the operator is serving. Sorted by the block at which they were last updated (content of updates below) in ascending order. - * This means the 'HEAD' (i.e. start) of the linked list will have the stalest 'updateBlock' value. - */ - mapping(address => StructuredLinkedListMock.List) internal _operatorToWhitelistedContractsByUpdate; - - /** - * operator => - * [ - * ( - * the least recent update block of all of the middlewares it's serving/served, - * latest time that the stake bonded at that update needed to serve until - * ) - * ] - */ - mapping(address => MiddlewareTimes[]) internal _operatorToMiddlewareTimes; - - constructor(IStrategyManager _strategyManager, IDelegationManager _delegation) { - strategyManager = _strategyManager; - delegation = _delegation; - _disableInitializers(); - } - - /// @notice Ensures that the operator has opted into slashing by the caller, and that the caller has never revoked its slashing ability. - modifier onlyRegisteredForService(address operator) { - require( - _whitelistedContractDetails[operator][msg.sender].contractCanSlashOperatorUntilBlock == MAX_CAN_SLASH_UNTIL, - "Slasher.onlyRegisteredForService: Operator has not opted into slashing by caller" - ); - _; - } - - // EXTERNAL FUNCTIONS - function initialize(address initialOwner, IPauserRegistry _pauserRegistry, uint256 initialPausedStatus) - external - initializer - { - _initializePauser(_pauserRegistry, initialPausedStatus); - _transferOwnership(initialOwner); - } - - /** - * @notice Gives the `contractAddress` permission to slash the funds of the caller. - * @dev Typically, this function must be called prior to registering for a middleware. - */ - function optIntoSlashing(address contractAddress) external onlyWhenNotPaused(PAUSED_OPT_INTO_SLASHING) { - //require(delegation.isOperator(msg.sender), "Slasher.optIntoSlashing: msg.sender is not a registered operator"); - _optIntoSlashing(msg.sender, contractAddress); - } - - /** - * @notice Used for 'slashing' a certain operator. - * @param toBeFrozen The operator to be frozen. - * @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop. - * @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`. - */ - function freezeOperator(address toBeFrozen) external onlyWhenNotPaused(PAUSED_NEW_FREEZING) { - require( - canSlash(toBeFrozen, msg.sender), - "Slasher.freezeOperator: msg.sender does not have permission to slash this operator" - ); - _freezeOperator(toBeFrozen, msg.sender); - } - - /** - * @notice Removes the 'frozen' status from each of the `frozenAddresses` - * @dev Callable only by the contract owner (i.e. governance). - */ - function resetFrozenStatus(address[] calldata frozenAddresses) external onlyOwner { - for (uint256 i = 0; i < frozenAddresses.length;) { - _resetFrozenStatus(frozenAddresses[i]); - unchecked { - ++i; - } - } - } - - /** - * @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration - * is slashable until serveUntilBlock - * @param operator the operator whose stake update is being recorded - * @param serveUntilBlock the block until which the operator's stake at the current block is slashable - * @dev adds the middleware's slashing contract to the operator's linked list - */ - function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) - external - onlyWhenNotPaused(PAUSED_FIRST_STAKE_UPDATE) - onlyRegisteredForService(operator) - { - // update the 'stalest' stakes update time + latest 'serveUntil' time of the `operator` - _recordUpdateAndAddToMiddlewareTimes(operator, uint32(block.number), serveUntilBlock); - - // Push the middleware to the end of the update list. This will fail if the caller *is* already in the list. - require( - _operatorToWhitelistedContractsByUpdate[operator].pushBack(_addressToUint(msg.sender)), - "Slasher.recordFirstStakeUpdate: Appending middleware unsuccessful" - ); - } - - /** - * @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals) - * to make sure the operator's stake at updateBlock is slashable until serveUntilBlock - * @param operator the operator whose stake update is being recorded - * @param updateBlock the block for which the stake update is being recorded - * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable - * @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after - * @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions, - * but it is anticipated to be rare and not detrimental. - */ - function recordStakeUpdate(address operator, uint32 updateBlock, uint32 serveUntilBlock, uint256 insertAfter) - external - onlyRegisteredForService(operator) - { - // sanity check on input - require(updateBlock <= block.number, "Slasher.recordStakeUpdate: cannot provide update for future block"); - // update the 'stalest' stakes update time + latest 'serveUntilBlock' of the `operator` - _recordUpdateAndAddToMiddlewareTimes(operator, updateBlock, serveUntilBlock); - - /** - * Move the middleware to its correct update position, determined by `updateBlock` and indicated via `insertAfter`. - * If the the middleware is the only one in the list, then no need to mutate the list - */ - if (_operatorToWhitelistedContractsByUpdate[operator].sizeOf() != 1) { - // Remove the caller (middleware) from the list. This will fail if the caller is *not* already in the list. - require( - _operatorToWhitelistedContractsByUpdate[operator].remove(_addressToUint(msg.sender)) != 0, - "Slasher.recordStakeUpdate: Removing middleware unsuccessful" - ); - // Run routine for updating the `operator`'s linked list of middlewares - _updateMiddlewareList(operator, updateBlock, insertAfter); - // if there is precisely one middleware in the list, then ensure that the caller is indeed the singular list entrant - } else { - require( - _operatorToWhitelistedContractsByUpdate[operator].getHead() == _addressToUint(msg.sender), - "Slasher.recordStakeUpdate: Caller is not the list entrant" - ); - } - } - - /** - * @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration - * is slashable until serveUntilBlock - * @param operator the operator whose stake update is being recorded - * @param serveUntilBlock the block until which the operator's stake at the current block is slashable - * @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to - * slash `operator` once `serveUntilBlock` is reached - */ - function recordLastStakeUpdateAndRevokeSlashingAbility(address operator, uint32 serveUntilBlock) - external - onlyRegisteredForService(operator) - { - // update the 'stalest' stakes update time + latest 'serveUntilBlock' of the `operator` - _recordUpdateAndAddToMiddlewareTimes(operator, uint32(block.number), serveUntilBlock); - // remove the middleware from the list - require( - _operatorToWhitelistedContractsByUpdate[operator].remove(_addressToUint(msg.sender)) != 0, - "Slasher.recordLastStakeUpdateAndRevokeSlashingAbility: Removing middleware unsuccessful" - ); - // revoke the middleware's ability to slash `operator` after `serverUntil` - _revokeSlashingAbility(operator, msg.sender, serveUntilBlock); - } - - // VIEW FUNCTIONS - - /// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`. - function contractCanSlashOperatorUntilBlock(address operator, address serviceContract) - external - view - returns (uint32) - { - return _whitelistedContractDetails[operator][serviceContract].contractCanSlashOperatorUntilBlock; - } - - /// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake - function latestUpdateBlock(address operator, address serviceContract) external view returns (uint32) { - return _whitelistedContractDetails[operator][serviceContract].latestUpdateBlock; - } - - /* - * @notice Returns `_whitelistedContractDetails[operator][serviceContract]`. - * @dev A getter function like this appears to be necessary for returning a struct from storage in struct form, rather than as a tuple. - */ - function whitelistedContractDetails(address operator, address serviceContract) - external - view - returns (MiddlewareDetails memory) - { - return _whitelistedContractDetails[operator][serviceContract]; - } - - /** - * @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to - * slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed - * and the staker's status is reset (to 'unfrozen'). - * @param staker The staker of interest. - * @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated - * to an operator who has their status set to frozen. Otherwise returns 'false'. - */ - function isFrozen(address staker) external view returns (bool) { - if (frozenStatus[staker]) { - return true; - } else if (delegation.isDelegated(staker)) { - address operatorAddress = delegation.delegatedTo(staker); - return (frozenStatus[operatorAddress]); - } else { - return false; - } - } - - /// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`. - function canSlash(address toBeSlashed, address slashingContract) public view returns (bool) { - if ( - block.number < _whitelistedContractDetails[toBeSlashed][slashingContract].contractCanSlashOperatorUntilBlock - ) { - return true; - } else { - return false; - } - } - - /** - * @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used - * to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `_operatorToMiddlewareTimes[operator]`). The specified - * struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal. - * This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event - * that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist. - * @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator, - * this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`. - * @param withdrawalStartBlock The block number at which the withdrawal was initiated. - * @param middlewareTimesIndex Indicates an index in `_operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw - * @dev The correct `middlewareTimesIndex` input should be computable off-chain. - */ - function canWithdraw(address operator, uint32 withdrawalStartBlock, uint256 middlewareTimesIndex) - external - view - returns (bool) - { - // if the operator has never registered for a middleware, just return 'true' - if (_operatorToMiddlewareTimes[operator].length == 0) { - return true; - } - - // pull the MiddlewareTimes struct at the `middlewareTimesIndex`th position in `_operatorToMiddlewareTimes[operator]` - MiddlewareTimes memory update = _operatorToMiddlewareTimes[operator][middlewareTimesIndex]; - - /** - * Case-handling for if the operator is not registered for any middlewares (i.e. they previously registered but are no longer registered for any), - * AND the withdrawal was initiated after the 'stalestUpdateBlock' of the MiddlewareTimes struct specified by the provided `middlewareTimesIndex`. - * NOTE: we check the 2nd of these 2 conditions first for gas efficiency, to help avoid an extra SLOAD in all other cases. - */ - if ( - withdrawalStartBlock >= update.stalestUpdateBlock - && _operatorToWhitelistedContractsByUpdate[operator].size == 0 - ) { - /** - * In this case, we just check against the 'latestServeUntilBlock' of the last MiddlewareTimes struct. This is because the operator not being registered - * for any middlewares (i.e. `_operatorToWhitelistedContractsByUpdate.size == 0`) means no new MiddlewareTimes structs will be being pushed, *and* the operator - * will not be undertaking any new obligations (so just checking against the last entry is OK, unlike when the operator is actively registered for >=1 middleware). - */ - update = _operatorToMiddlewareTimes[operator][_operatorToMiddlewareTimes[operator].length - 1]; - return (uint32(block.number) > update.latestServeUntilBlock); - } - - /** - * Make sure the stalest update block at the time of the update is strictly after `withdrawalStartBlock` and ensure that the current time - * is after the `latestServeUntilBlock` of the update. This assures us that this that all middlewares were updated after the withdrawal began, and - * that the stake is no longer slashable. - */ - return (withdrawalStartBlock < update.stalestUpdateBlock && uint32(block.number) > update.latestServeUntilBlock); - } - - /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][arrayIndex]`. - function operatorToMiddlewareTimes(address operator, uint256 arrayIndex) - external - view - returns (MiddlewareTimes memory) - { - return _operatorToMiddlewareTimes[operator][arrayIndex]; - } - - /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator].length`. - function middlewareTimesLength(address operator) external view returns (uint256) { - return _operatorToMiddlewareTimes[operator].length; - } - - /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`. - function getMiddlewareTimesIndexStalestUpdateBlock(address operator, uint32 index) external view returns (uint32) { - return _operatorToMiddlewareTimes[operator][index].stalestUpdateBlock; - } - - /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][index].latestServeUntilBlock`. - function getMiddlewareTimesIndexServeUntilBlock(address operator, uint32 index) external view returns (uint32) { - return _operatorToMiddlewareTimes[operator][index].latestServeUntilBlock; - } - - /// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`. - function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256) { - return _operatorToWhitelistedContractsByUpdate[operator].size; - } - - /// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`). - function operatorWhitelistedContractsLinkedListEntry(address operator, address node) - external - view - returns (bool, uint256, uint256) - { - return StructuredLinkedListMock.getNode(_operatorToWhitelistedContractsByUpdate[operator], _addressToUint(node)); - } - - /** - * @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`. - * @dev Used within this contract only as a fallback in the case when an incorrect value of `insertAfter` is supplied as an input to `_updateMiddlewareList`. - * @dev The return value should *either* be 'HEAD' (i.e. zero) in the event that the node being inserted in the linked list has an `updateBlock` - * that is less than the HEAD of the list, *or* the return value should specify the last `node` in the linked list for which - * `_whitelistedContractDetails[operator][node].latestUpdateBlock <= updateBlock`, - * i.e. the node such that the *next* node either doesn't exist, - * OR - * `_whitelistedContractDetails[operator][nextNode].latestUpdateBlock > updateBlock`. - */ - function getCorrectValueForInsertAfter(address operator, uint32 updateBlock) public view returns (uint256) { - uint256 node = _operatorToWhitelistedContractsByUpdate[operator].getHead(); - /** - * Special case: - * If the node being inserted in the linked list has an `updateBlock` that is less than the HEAD of the list, then we set `insertAfter = HEAD`. - * In _updateMiddlewareList(), the new node will be pushed to the front (HEAD) of the list. - */ - if (_whitelistedContractDetails[operator][_uintToAddress(node)].latestUpdateBlock > updateBlock) { - return HEAD; - } - /** - * `node` being zero (i.e. equal to 'HEAD') indicates an empty/non-existent node, i.e. reaching the end of the linked list. - * Since the linked list is ordered in ascending order of update blocks, we simply start from the head of the list and step through until - * we find a the *last* `node` for which `_whitelistedContractDetails[operator][node].latestUpdateBlock <= updateBlock`, or - * otherwise reach the end of the list. - */ - (, uint256 nextNode) = _operatorToWhitelistedContractsByUpdate[operator].getNextNode(node); - while ( - (nextNode != HEAD) - && (_whitelistedContractDetails[operator][_uintToAddress(node)].latestUpdateBlock <= updateBlock) - ) { - node = nextNode; - (, nextNode) = _operatorToWhitelistedContractsByUpdate[operator].getNextNode(node); - } - return node; - } - - /// @notice gets the node previous to the given node in the operators middleware update linked list - /// @dev used in offchain libs for updating stakes - function getPreviousWhitelistedContractByUpdate(address operator, uint256 node) - external - view - returns (bool, uint256) - { - return _operatorToWhitelistedContractsByUpdate[operator].getPreviousNode(node); - } - - // INTERNAL FUNCTIONS - - function _optIntoSlashing(address operator, address contractAddress) internal { - //allow the contract to slash anytime before a time VERY far in the future - _whitelistedContractDetails[operator][contractAddress].contractCanSlashOperatorUntilBlock = MAX_CAN_SLASH_UNTIL; - emit OptedIntoSlashing(operator, contractAddress); - } - - function _revokeSlashingAbility(address operator, address contractAddress, uint32 serveUntilBlock) internal { - require( - serveUntilBlock != MAX_CAN_SLASH_UNTIL, - "Slasher._revokeSlashingAbility: serveUntilBlock time must be limited" - ); - // contractAddress can now only slash operator before `serveUntilBlock` - _whitelistedContractDetails[operator][contractAddress].contractCanSlashOperatorUntilBlock = serveUntilBlock; - emit SlashingAbilityRevoked(operator, contractAddress, serveUntilBlock); - } - - function _freezeOperator(address toBeFrozen, address slashingContract) internal { - if (!frozenStatus[toBeFrozen]) { - frozenStatus[toBeFrozen] = true; - emit OperatorFrozen(toBeFrozen, slashingContract); - } - } - - function _resetFrozenStatus(address previouslySlashedAddress) internal { - if (frozenStatus[previouslySlashedAddress]) { - frozenStatus[previouslySlashedAddress] = false; - emit FrozenStatusReset(previouslySlashedAddress); - } - } - - /** - * @notice records the most recent updateBlock for the currently updating middleware and appends an entry to the operator's list of - * MiddlewareTimes if relevant information has updated - * @param operator the entity whose stake update is being recorded - * @param updateBlock the block number for which the currently updating middleware is updating the serveUntilBlock for - * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable - * @dev this function is only called during externally called stake updates by middleware contracts that can slash operator - */ - function _recordUpdateAndAddToMiddlewareTimes(address operator, uint32 updateBlock, uint32 serveUntilBlock) - internal - { - // reject any stale update, i.e. one from before that of the most recent recorded update for the currently updating middleware - require( - _whitelistedContractDetails[operator][msg.sender].latestUpdateBlock <= updateBlock, - "Slasher._recordUpdateAndAddToMiddlewareTimes: can't push a previous update" - ); - _whitelistedContractDetails[operator][msg.sender].latestUpdateBlock = updateBlock; - // get the latest recorded MiddlewareTimes, if the operator's list of MiddlwareTimes is non empty - MiddlewareTimes memory curr; - uint256 _operatorToMiddlewareTimesLength = _operatorToMiddlewareTimes[operator].length; - if (_operatorToMiddlewareTimesLength != 0) { - curr = _operatorToMiddlewareTimes[operator][_operatorToMiddlewareTimesLength - 1]; - } - MiddlewareTimes memory next = curr; - bool pushToMiddlewareTimes; - // if the serve until is later than the latest recorded one, update it - if (serveUntilBlock > curr.latestServeUntilBlock) { - next.latestServeUntilBlock = serveUntilBlock; - // mark that we need push next to middleware times array because it contains new information - pushToMiddlewareTimes = true; - } - - // If this is the very first middleware added to the operator's list of middleware, then we add an entry to _operatorToMiddlewareTimes - if (_operatorToWhitelistedContractsByUpdate[operator].size == 0) { - next.stalestUpdateBlock = updateBlock; - pushToMiddlewareTimes = true; - } - // If the middleware is the first in the list, we will update the `stalestUpdateBlock` field in MiddlewareTimes - else if (_operatorToWhitelistedContractsByUpdate[operator].getHead() == _addressToUint(msg.sender)) { - // if the updated middleware was the earliest update, set it to the 2nd earliest update's update time - (bool hasNext, uint256 nextNode) = - _operatorToWhitelistedContractsByUpdate[operator].getNextNode(_addressToUint(msg.sender)); - - if (hasNext) { - // get the next middleware's latest update block - uint32 nextMiddlewaresLeastRecentUpdateBlock = - _whitelistedContractDetails[operator][_uintToAddress(nextNode)].latestUpdateBlock; - if (nextMiddlewaresLeastRecentUpdateBlock < updateBlock) { - // if there is a next node, then set the stalestUpdateBlock to its recorded value - next.stalestUpdateBlock = nextMiddlewaresLeastRecentUpdateBlock; - } else { - //otherwise updateBlock is the least recent update as well - next.stalestUpdateBlock = updateBlock; - } - } else { - // otherwise this is the only middleware so right now is the stalestUpdateBlock - next.stalestUpdateBlock = updateBlock; - } - // mark that we need to push `next` to middleware times array because it contains new information - pushToMiddlewareTimes = true; - } - - // if `next` has new information, then push it - if (pushToMiddlewareTimes) { - _operatorToMiddlewareTimes[operator].push(next); - emit MiddlewareTimesAdded( - operator, - _operatorToMiddlewareTimes[operator].length - 1, - next.stalestUpdateBlock, - next.latestServeUntilBlock - ); - } - } - - /// @notice A routine for updating the `operator`'s linked list of middlewares, inside `recordStakeUpdate`. - function _updateMiddlewareList(address operator, uint32 updateBlock, uint256 insertAfter) internal { - /** - * boolean used to track if the `insertAfter input to this function is incorrect. If it is, then `runFallbackRoutine` will - * be flipped to 'true', and we will use `getCorrectValueForInsertAfter` to find the correct input. This routine helps solve - * a race condition where the proper value of `insertAfter` changes while a transaction is pending. - */ - bool runFallbackRoutine = false; - // If this condition is met, then the `updateBlock` input should be after `insertAfter`'s latest updateBlock - if (insertAfter != HEAD) { - // Check that `insertAfter` exists. If not, we will use the fallback routine to find the correct value for `insertAfter`. - if (!_operatorToWhitelistedContractsByUpdate[operator].nodeExists(insertAfter)) { - runFallbackRoutine = true; - } - - /** - * Make sure `insertAfter` specifies a node for which the most recent updateBlock was *at or before* updateBlock. - * Again, if not, we will use the fallback routine to find the correct value for `insertAfter`. - */ - if ( - (!runFallbackRoutine) - && (_whitelistedContractDetails[operator][_uintToAddress(insertAfter)].latestUpdateBlock > updateBlock) - ) { - runFallbackRoutine = true; - } - - // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct so far - if (!runFallbackRoutine) { - // Get `insertAfter`'s successor. `hasNext` will be false if `insertAfter` is the last node in the list - (bool hasNext, uint256 nextNode) = - _operatorToWhitelistedContractsByUpdate[operator].getNextNode(insertAfter); - if (hasNext) { - /** - * Make sure the element after `insertAfter`'s most recent updateBlock was *strictly after* `updateBlock`. - * Again, if not, we will use the fallback routine to find the correct value for `insertAfter`. - */ - if ( - _whitelistedContractDetails[operator][_uintToAddress(nextNode)].latestUpdateBlock <= updateBlock - ) { - runFallbackRoutine = true; - } - } - } - - // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct on all counts - if (!runFallbackRoutine) { - /** - * Insert the caller (middleware) after `insertAfter`. - * This will fail if `msg.sender` is already in the list, which they shouldn't be because they were removed from the list above. - */ - require( - _operatorToWhitelistedContractsByUpdate[operator].insertAfter( - insertAfter, _addressToUint(msg.sender) - ), - "Slasher.recordStakeUpdate: Inserting middleware unsuccessful" - ); - // in this case (runFallbackRoutine == true), we run a search routine to find the correct input value of `insertAfter` and then rerun this function - } else { - insertAfter = getCorrectValueForInsertAfter(operator, updateBlock); - _updateMiddlewareList(operator, updateBlock, insertAfter); - } - // In this case (insertAfter == HEAD), the `updateBlock` input should be before every other middleware's latest updateBlock. - } else { - /** - * Check that `updateBlock` is before any other middleware's latest updateBlock. - * If not, use the fallback routine to find the correct value for `insertAfter`. - */ - if ( - _whitelistedContractDetails[operator][_uintToAddress( - _operatorToWhitelistedContractsByUpdate[operator].getHead() - )].latestUpdateBlock <= updateBlock - ) { - runFallbackRoutine = true; - } - // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct on all counts - if (!runFallbackRoutine) { - /** - * Insert the middleware at the start (i.e. HEAD) of the list. - * This will fail if `msg.sender` is already in the list, which they shouldn't be because they were removed from the list above. - */ - require( - _operatorToWhitelistedContractsByUpdate[operator].pushFront(_addressToUint(msg.sender)), - "Slasher.recordStakeUpdate: Preppending middleware unsuccessful" - ); - // in this case (runFallbackRoutine == true), we run a search routine to find the correct input value of `insertAfter` and then rerun this function - } else { - insertAfter = getCorrectValueForInsertAfter(operator, updateBlock); - _updateMiddlewareList(operator, updateBlock, insertAfter); - } - } - } - - function _addressToUint(address addr) internal pure returns (uint256) { - return uint256(uint160(addr)); - } - - function _uintToAddress(uint256 x) internal pure returns (address) { - return address(uint160(x)); - } - - /** - * @dev This empty reserved space is put in place to allow future versions to add new - * variables without shifting down storage in the inheritance chain. - * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - */ - uint256[46] private __gap; -} +// // SPDX-License-Identifier: BUSL-1.1 +// pragma solidity >=0.8.12; + +// import "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +// import "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +// import "src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; +// import "test/mocks/StructuredLinkedListMock.sol"; +// import "test/mocks/PausableMock.sol"; +// import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +// import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +// struct MiddlewareDetails { +// uint32 contractCanSlashOperatorUntilBlock; +// uint32 latestUpdateBlock; +// } + +// /** +// * @title The primary 'slashing' contract for EigenLayer. +// * @author Layr Labs, Inc. +// * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service +// * @notice This contract specifies details on slashing. The functionalities are: +// * - adding contracts who have permission to perform slashing, +// * - revoking permission for slashing from specified contracts, +// * - tracking historic stake updates to ensure that withdrawals can only be completed once no middlewares have slashing rights +// * over the funds being withdrawn +// */ +// contract SlasherMock is Initializable, OwnableUpgradeable, IAllocationManager { +// using StructuredLinkedListMock for StructuredLinkedListMock.List; + +// uint256 private constant HEAD = 0; + +// uint8 internal constant PAUSED_OPT_INTO_SLASHING = 0; +// uint8 internal constant PAUSED_FIRST_STAKE_UPDATE = 1; +// uint8 internal constant PAUSED_NEW_FREEZING = 2; + +// /// @notice The central StrategyManager contract of EigenLayer +// IStrategyManager public immutable strategyManager; +// /// @notice The DelegationManager contract of EigenLayer +// IDelegationManager public immutable delegation; +// // operator => whitelisted contract with slashing permissions => (the time before which the contract is allowed to slash the user, block it was last updated) +// mapping(address => mapping(address => MiddlewareDetails)) internal _whitelistedContractDetails; +// // staker => if their funds are 'frozen' and potentially subject to slashing or not +// mapping(address => bool) internal frozenStatus; + +// uint32 internal constant MAX_CAN_SLASH_UNTIL = type(uint32).max; + +// /** +// * operator => a linked list of the addresses of the whitelisted middleware with permission to slash the operator, i.e. which +// * the operator is serving. Sorted by the block at which they were last updated (content of updates below) in ascending order. +// * This means the 'HEAD' (i.e. start) of the linked list will have the stalest 'updateBlock' value. +// */ +// mapping(address => StructuredLinkedListMock.List) internal _operatorToWhitelistedContractsByUpdate; + +// /** +// * operator => +// * [ +// * ( +// * the least recent update block of all of the middlewares it's serving/served, +// * latest time that the stake bonded at that update needed to serve until +// * ) +// * ] +// */ +// mapping(address => MiddlewareDetails[]) internal _operatorToMiddlewareTimes; + +// constructor(IStrategyManager _strategyManager, IDelegationManager _delegation) { +// strategyManager = _strategyManager; +// delegation = _delegation; +// _disableInitializers(); +// } + +// /// @notice Ensures that the operator has opted into slashing by the caller, and that the caller has never revoked its slashing ability. +// modifier onlyRegisteredForService(address operator) { +// require( +// _whitelistedContractDetails[operator][msg.sender].contractCanSlashOperatorUntilBlock == MAX_CAN_SLASH_UNTIL, +// "Slasher.onlyRegisteredForService: Operator has not opted into slashing by caller" +// ); +// _; +// } + +// // EXTERNAL FUNCTIONS +// function initialize(address initialOwner, IPauserRegistry _pauserRegistry, uint256 initialPausedStatus) +// external +// initializer +// { +// _initializePauser(_pauserRegistry, initialPausedStatus); +// _transferOwnership(initialOwner); +// } + +// /** +// * @notice Gives the `contractAddress` permission to slash the funds of the caller. +// * @dev Typically, this function must be called prior to registering for a middleware. +// */ +// function optIntoSlashing(address contractAddress) external { +// //require(delegation.isOperator(msg.sender), "Slasher.optIntoSlashing: msg.sender is not a registered operator"); +// _optIntoSlashing(msg.sender, contractAddress); +// } + +// /** +// * @notice Used for 'slashing' a certain operator. +// * @param toBeFrozen The operator to be frozen. +// * @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop. +// * @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`. +// */ +// function freezeOperator(address toBeFrozen) external { +// require( +// canSlash(toBeFrozen, msg.sender), +// "Slasher.freezeOperator: msg.sender does not have permission to slash this operator" +// ); +// _freezeOperator(toBeFrozen, msg.sender); +// } + +// /** +// * @notice Removes the 'frozen' status from each of the `frozenAddresses` +// * @dev Callable only by the contract owner (i.e. governance). +// */ +// function resetFrozenStatus(address[] calldata frozenAddresses) external onlyOwner { +// for (uint256 i = 0; i < frozenAddresses.length;) { +// _resetFrozenStatus(frozenAddresses[i]); +// unchecked { +// ++i; +// } +// } +// } + +// /** +// * @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration +// * is slashable until serveUntilBlock +// * @param operator the operator whose stake update is being recorded +// * @param serveUntilBlock the block until which the operator's stake at the current block is slashable +// * @dev adds the middleware's slashing contract to the operator's linked list +// */ +// function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) +// external +// onlyRegisteredForService(operator) +// { +// // update the 'stalest' stakes update time + latest 'serveUntil' time of the `operator` +// _recordUpdateAndAddToMiddlewareTimes(operator, uint32(block.number), serveUntilBlock); + +// // Push the middleware to the end of the update list. This will fail if the caller *is* already in the list. +// require( +// _operatorToWhitelistedContractsByUpdate[operator].pushBack(_addressToUint(msg.sender)), +// "Slasher.recordFirstStakeUpdate: Appending middleware unsuccessful" +// ); +// } + +// /** +// * @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals) +// * to make sure the operator's stake at updateBlock is slashable until serveUntilBlock +// * @param operator the operator whose stake update is being recorded +// * @param updateBlock the block for which the stake update is being recorded +// * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable +// * @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after +// * @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions, +// * but it is anticipated to be rare and not detrimental. +// */ +// function recordStakeUpdate(address operator, uint32 updateBlock, uint32 serveUntilBlock, uint256 insertAfter) +// external +// onlyRegisteredForService(operator) +// { +// // sanity check on input +// require(updateBlock <= block.number, "Slasher.recordStakeUpdate: cannot provide update for future block"); +// // update the 'stalest' stakes update time + latest 'serveUntilBlock' of the `operator` +// _recordUpdateAndAddToMiddlewareTimes(operator, updateBlock, serveUntilBlock); + +// /** +// * Move the middleware to its correct update position, determined by `updateBlock` and indicated via `insertAfter`. +// * If the the middleware is the only one in the list, then no need to mutate the list +// */ +// if (_operatorToWhitelistedContractsByUpdate[operator].sizeOf() != 1) { +// // Remove the caller (middleware) from the list. This will fail if the caller is *not* already in the list. +// require( +// _operatorToWhitelistedContractsByUpdate[operator].remove(_addressToUint(msg.sender)) != 0, +// "Slasher.recordStakeUpdate: Removing middleware unsuccessful" +// ); +// // Run routine for updating the `operator`'s linked list of middlewares +// _updateMiddlewareList(operator, updateBlock, insertAfter); +// // if there is precisely one middleware in the list, then ensure that the caller is indeed the singular list entrant +// } else { +// require( +// _operatorToWhitelistedContractsByUpdate[operator].getHead() == _addressToUint(msg.sender), +// "Slasher.recordStakeUpdate: Caller is not the list entrant" +// ); +// } +// } + +// /** +// * @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration +// * is slashable until serveUntilBlock +// * @param operator the operator whose stake update is being recorded +// * @param serveUntilBlock the block until which the operator's stake at the current block is slashable +// * @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to +// * slash `operator` once `serveUntilBlock` is reached +// */ +// function recordLastStakeUpdateAndRevokeSlashingAbility(address operator, uint32 serveUntilBlock) +// external +// onlyRegisteredForService(operator) +// { +// // update the 'stalest' stakes update time + latest 'serveUntilBlock' of the `operator` +// _recordUpdateAndAddToMiddlewareTimes(operator, uint32(block.number), serveUntilBlock); +// // remove the middleware from the list +// require( +// _operatorToWhitelistedContractsByUpdate[operator].remove(_addressToUint(msg.sender)) != 0, +// "Slasher.recordLastStakeUpdateAndRevokeSlashingAbility: Removing middleware unsuccessful" +// ); +// // revoke the middleware's ability to slash `operator` after `serverUntil` +// _revokeSlashingAbility(operator, msg.sender, serveUntilBlock); +// } + +// // VIEW FUNCTIONS + +// /// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`. +// function contractCanSlashOperatorUntilBlock(address operator, address serviceContract) +// external +// view +// returns (uint32) +// { +// return _whitelistedContractDetails[operator][serviceContract].contractCanSlashOperatorUntilBlock; +// } + +// /// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake +// function latestUpdateBlock(address operator, address serviceContract) external view returns (uint32) { +// return _whitelistedContractDetails[operator][serviceContract].latestUpdateBlock; +// } + +// /* +// * @notice Returns `_whitelistedContractDetails[operator][serviceContract]`. +// * @dev A getter function like this appears to be necessary for returning a struct from storage in struct form, rather than as a tuple. +// */ +// function whitelistedContractDetails(address operator, address serviceContract) +// external +// view +// returns (MiddlewareDetails memory) +// { +// return _whitelistedContractDetails[operator][serviceContract]; +// } + +// /** +// * @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to +// * slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed +// * and the staker's status is reset (to 'unfrozen'). +// * @param staker The staker of interest. +// * @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated +// * to an operator who has their status set to frozen. Otherwise returns 'false'. +// */ +// function isFrozen(address staker) external view returns (bool) { +// if (frozenStatus[staker]) { +// return true; +// } else if (delegation.isDelegated(staker)) { +// address operatorAddress = delegation.delegatedTo(staker); +// return (frozenStatus[operatorAddress]); +// } else { +// return false; +// } +// } + +// /// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`. +// function canSlash(address toBeSlashed, address slashingContract) public view returns (bool) { +// if ( +// block.number < _whitelistedContractDetails[toBeSlashed][slashingContract].contractCanSlashOperatorUntilBlock +// ) { +// return true; +// } else { +// return false; +// } +// } + +// /** +// * @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used +// * to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `_operatorToMiddlewareTimes[operator]`). The specified +// * struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal. +// * This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event +// * that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist. +// * @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator, +// * this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`. +// * @param withdrawalStartBlock The block number at which the withdrawal was initiated. +// * @param middlewareTimesIndex Indicates an index in `_operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw +// * @dev The correct `middlewareTimesIndex` input should be computable off-chain. +// */ +// function canWithdraw(address operator, uint32 withdrawalStartBlock, uint256 middlewareTimesIndex) +// external +// view +// returns (bool) +// { +// // if the operator has never registered for a middleware, just return 'true' +// if (_operatorToMiddlewareTimes[operator].length == 0) { +// return true; +// } + +// // pull the MiddlewareTimes struct at the `middlewareTimesIndex`th position in `_operatorToMiddlewareTimes[operator]` +// MiddlewareTimes memory update = _operatorToMiddlewareTimes[operator][middlewareTimesIndex]; + +// /** +// * Case-handling for if the operator is not registered for any middlewares (i.e. they previously registered but are no longer registered for any), +// * AND the withdrawal was initiated after the 'stalestUpdateBlock' of the MiddlewareTimes struct specified by the provided `middlewareTimesIndex`. +// * NOTE: we check the 2nd of these 2 conditions first for gas efficiency, to help avoid an extra SLOAD in all other cases. +// */ +// if ( +// withdrawalStartBlock >= update.stalestUpdateBlock +// && _operatorToWhitelistedContractsByUpdate[operator].size == 0 +// ) { +// /** +// * In this case, we just check against the 'latestServeUntilBlock' of the last MiddlewareTimes struct. This is because the operator not being registered +// * for any middlewares (i.e. `_operatorToWhitelistedContractsByUpdate.size == 0`) means no new MiddlewareTimes structs will be being pushed, *and* the operator +// * will not be undertaking any new obligations (so just checking against the last entry is OK, unlike when the operator is actively registered for >=1 middleware). +// */ +// update = _operatorToMiddlewareTimes[operator][_operatorToMiddlewareTimes[operator].length - 1]; +// return (uint32(block.number) > update.latestServeUntilBlock); +// } + +// /** +// * Make sure the stalest update block at the time of the update is strictly after `withdrawalStartBlock` and ensure that the current time +// * is after the `latestServeUntilBlock` of the update. This assures us that this that all middlewares were updated after the withdrawal began, and +// * that the stake is no longer slashable. +// */ +// return (withdrawalStartBlock < update.stalestUpdateBlock && uint32(block.number) > update.latestServeUntilBlock); +// } + +// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][arrayIndex]`. +// function operatorToMiddlewareTimes(address operator, uint256 arrayIndex) +// external +// view +// returns (MiddlewareDetails memory) +// { +// return _operatorToMiddlewareTimes[operator][arrayIndex]; +// } + +// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator].length`. +// function middlewareTimesLength(address operator) external view returns (uint256) { +// return _operatorToMiddlewareTimes[operator].length; +// } + +// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`. +// function getMiddlewareTimesIndexStalestUpdateBlock(address operator, uint32 index) external view returns (uint32) { +// return _operatorToMiddlewareTimes[operator][index].stalestUpdateBlock; +// } + +// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][index].latestServeUntilBlock`. +// function getMiddlewareTimesIndexServeUntilBlock(address operator, uint32 index) external view returns (uint32) { +// return _operatorToMiddlewareTimes[operator][index].latestServeUntilBlock; +// } + +// /// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`. +// function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256) { +// return _operatorToWhitelistedContractsByUpdate[operator].size; +// } + +// /// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`). +// function operatorWhitelistedContractsLinkedListEntry(address operator, address node) +// external +// view +// returns (bool, uint256, uint256) +// { +// return StructuredLinkedListMock.getNode(_operatorToWhitelistedContractsByUpdate[operator], _addressToUint(node)); +// } + +// /** +// * @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`. +// * @dev Used within this contract only as a fallback in the case when an incorrect value of `insertAfter` is supplied as an input to `_updateMiddlewareList`. +// * @dev The return value should *either* be 'HEAD' (i.e. zero) in the event that the node being inserted in the linked list has an `updateBlock` +// * that is less than the HEAD of the list, *or* the return value should specify the last `node` in the linked list for which +// * `_whitelistedContractDetails[operator][node].latestUpdateBlock <= updateBlock`, +// * i.e. the node such that the *next* node either doesn't exist, +// * OR +// * `_whitelistedContractDetails[operator][nextNode].latestUpdateBlock > updateBlock`. +// */ +// function getCorrectValueForInsertAfter(address operator, uint32 updateBlock) public view returns (uint256) { +// uint256 node = _operatorToWhitelistedContractsByUpdate[operator].getHead(); +// /** +// * Special case: +// * If the node being inserted in the linked list has an `updateBlock` that is less than the HEAD of the list, then we set `insertAfter = HEAD`. +// * In _updateMiddlewareList(), the new node will be pushed to the front (HEAD) of the list. +// */ +// if (_whitelistedContractDetails[operator][_uintToAddress(node)].latestUpdateBlock > updateBlock) { +// return HEAD; +// } +// /** +// * `node` being zero (i.e. equal to 'HEAD') indicates an empty/non-existent node, i.e. reaching the end of the linked list. +// * Since the linked list is ordered in ascending order of update blocks, we simply start from the head of the list and step through until +// * we find a the *last* `node` for which `_whitelistedContractDetails[operator][node].latestUpdateBlock <= updateBlock`, or +// * otherwise reach the end of the list. +// */ +// (, uint256 nextNode) = _operatorToWhitelistedContractsByUpdate[operator].getNextNode(node); +// while ( +// (nextNode != HEAD) +// && (_whitelistedContractDetails[operator][_uintToAddress(node)].latestUpdateBlock <= updateBlock) +// ) { +// node = nextNode; +// (, nextNode) = _operatorToWhitelistedContractsByUpdate[operator].getNextNode(node); +// } +// return node; +// } + +// /// @notice gets the node previous to the given node in the operators middleware update linked list +// /// @dev used in offchain libs for updating stakes +// function getPreviousWhitelistedContractByUpdate(address operator, uint256 node) +// external +// view +// returns (bool, uint256) +// { +// return _operatorToWhitelistedContractsByUpdate[operator].getPreviousNode(node); +// } + +// // INTERNAL FUNCTIONS + +// function _optIntoSlashing(address operator, address contractAddress) internal { +// //allow the contract to slash anytime before a time VERY far in the future +// _whitelistedContractDetails[operator][contractAddress].contractCanSlashOperatorUntilBlock = MAX_CAN_SLASH_UNTIL; +// emit OptedIntoSlashing(operator, contractAddress); +// } + +// function _revokeSlashingAbility(address operator, address contractAddress, uint32 serveUntilBlock) internal { +// require( +// serveUntilBlock != MAX_CAN_SLASH_UNTIL, +// "Slasher._revokeSlashingAbility: serveUntilBlock time must be limited" +// ); +// // contractAddress can now only slash operator before `serveUntilBlock` +// _whitelistedContractDetails[operator][contractAddress].contractCanSlashOperatorUntilBlock = serveUntilBlock; +// emit SlashingAbilityRevoked(operator, contractAddress, serveUntilBlock); +// } + +// function _freezeOperator(address toBeFrozen, address slashingContract) internal { +// if (!frozenStatus[toBeFrozen]) { +// frozenStatus[toBeFrozen] = true; +// emit OperatorFrozen(toBeFrozen, slashingContract); +// } +// } + +// function _resetFrozenStatus(address previouslySlashedAddress) internal { +// if (frozenStatus[previouslySlashedAddress]) { +// frozenStatus[previouslySlashedAddress] = false; +// emit FrozenStatusReset(previouslySlashedAddress); +// } +// } + +// /** +// * @notice records the most recent updateBlock for the currently updating middleware and appends an entry to the operator's list of +// * MiddlewareTimes if relevant information has updated +// * @param operator the entity whose stake update is being recorded +// * @param updateBlock the block number for which the currently updating middleware is updating the serveUntilBlock for +// * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable +// * @dev this function is only called during externally called stake updates by middleware contracts that can slash operator +// */ +// function _recordUpdateAndAddToMiddlewareTimes(address operator, uint32 updateBlock, uint32 serveUntilBlock) +// internal +// { +// // reject any stale update, i.e. one from before that of the most recent recorded update for the currently updating middleware +// require( +// _whitelistedContractDetails[operator][msg.sender].latestUpdateBlock <= updateBlock, +// "Slasher._recordUpdateAndAddToMiddlewareTimes: can't push a previous update" +// ); +// _whitelistedContractDetails[operator][msg.sender].latestUpdateBlock = updateBlock; +// // get the latest recorded MiddlewareTimes, if the operator's list of MiddlwareTimes is non empty +// MiddlewareTimes memory curr; +// uint256 _operatorToMiddlewareTimesLength = _operatorToMiddlewareTimes[operator].length; +// if (_operatorToMiddlewareTimesLength != 0) { +// curr = _operatorToMiddlewareTimes[operator][_operatorToMiddlewareTimesLength - 1]; +// } +// MiddlewareTimes memory next = curr; +// bool pushToMiddlewareTimes; +// // if the serve until is later than the latest recorded one, update it +// if (serveUntilBlock > curr.latestServeUntilBlock) { +// next.latestServeUntilBlock = serveUntilBlock; +// // mark that we need push next to middleware times array because it contains new information +// pushToMiddlewareTimes = true; +// } + +// // If this is the very first middleware added to the operator's list of middleware, then we add an entry to _operatorToMiddlewareTimes +// if (_operatorToWhitelistedContractsByUpdate[operator].size == 0) { +// next.stalestUpdateBlock = updateBlock; +// pushToMiddlewareTimes = true; +// } +// // If the middleware is the first in the list, we will update the `stalestUpdateBlock` field in MiddlewareTimes +// else if (_operatorToWhitelistedContractsByUpdate[operator].getHead() == _addressToUint(msg.sender)) { +// // if the updated middleware was the earliest update, set it to the 2nd earliest update's update time +// (bool hasNext, uint256 nextNode) = +// _operatorToWhitelistedContractsByUpdate[operator].getNextNode(_addressToUint(msg.sender)); + +// if (hasNext) { +// // get the next middleware's latest update block +// uint32 nextMiddlewaresLeastRecentUpdateBlock = +// _whitelistedContractDetails[operator][_uintToAddress(nextNode)].latestUpdateBlock; +// if (nextMiddlewaresLeastRecentUpdateBlock < updateBlock) { +// // if there is a next node, then set the stalestUpdateBlock to its recorded value +// next.stalestUpdateBlock = nextMiddlewaresLeastRecentUpdateBlock; +// } else { +// //otherwise updateBlock is the least recent update as well +// next.stalestUpdateBlock = updateBlock; +// } +// } else { +// // otherwise this is the only middleware so right now is the stalestUpdateBlock +// next.stalestUpdateBlock = updateBlock; +// } +// // mark that we need to push `next` to middleware times array because it contains new information +// pushToMiddlewareTimes = true; +// } + +// // if `next` has new information, then push it +// if (pushToMiddlewareTimes) { +// _operatorToMiddlewareTimes[operator].push(next); +// emit MiddlewareTimesAdded( +// operator, +// _operatorToMiddlewareTimes[operator].length - 1, +// next.stalestUpdateBlock, +// next.latestServeUntilBlock +// ); +// } +// } + +// /// @notice A routine for updating the `operator`'s linked list of middlewares, inside `recordStakeUpdate`. +// function _updateMiddlewareList(address operator, uint32 updateBlock, uint256 insertAfter) internal { +// /** +// * boolean used to track if the `insertAfter input to this function is incorrect. If it is, then `runFallbackRoutine` will +// * be flipped to 'true', and we will use `getCorrectValueForInsertAfter` to find the correct input. This routine helps solve +// * a race condition where the proper value of `insertAfter` changes while a transaction is pending. +// */ +// bool runFallbackRoutine = false; +// // If this condition is met, then the `updateBlock` input should be after `insertAfter`'s latest updateBlock +// if (insertAfter != HEAD) { +// // Check that `insertAfter` exists. If not, we will use the fallback routine to find the correct value for `insertAfter`. +// if (!_operatorToWhitelistedContractsByUpdate[operator].nodeExists(insertAfter)) { +// runFallbackRoutine = true; +// } + +// /** +// * Make sure `insertAfter` specifies a node for which the most recent updateBlock was *at or before* updateBlock. +// * Again, if not, we will use the fallback routine to find the correct value for `insertAfter`. +// */ +// if ( +// (!runFallbackRoutine) +// && (_whitelistedContractDetails[operator][_uintToAddress(insertAfter)].latestUpdateBlock > updateBlock) +// ) { +// runFallbackRoutine = true; +// } + +// // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct so far +// if (!runFallbackRoutine) { +// // Get `insertAfter`'s successor. `hasNext` will be false if `insertAfter` is the last node in the list +// (bool hasNext, uint256 nextNode) = +// _operatorToWhitelistedContractsByUpdate[operator].getNextNode(insertAfter); +// if (hasNext) { +// /** +// * Make sure the element after `insertAfter`'s most recent updateBlock was *strictly after* `updateBlock`. +// * Again, if not, we will use the fallback routine to find the correct value for `insertAfter`. +// */ +// if ( +// _whitelistedContractDetails[operator][_uintToAddress(nextNode)].latestUpdateBlock <= updateBlock +// ) { +// runFallbackRoutine = true; +// } +// } +// } + +// // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct on all counts +// if (!runFallbackRoutine) { +// /** +// * Insert the caller (middleware) after `insertAfter`. +// * This will fail if `msg.sender` is already in the list, which they shouldn't be because they were removed from the list above. +// */ +// require( +// _operatorToWhitelistedContractsByUpdate[operator].insertAfter( +// insertAfter, _addressToUint(msg.sender) +// ), +// "Slasher.recordStakeUpdate: Inserting middleware unsuccessful" +// ); +// // in this case (runFallbackRoutine == true), we run a search routine to find the correct input value of `insertAfter` and then rerun this function +// } else { +// insertAfter = getCorrectValueForInsertAfter(operator, updateBlock); +// _updateMiddlewareList(operator, updateBlock, insertAfter); +// } +// // In this case (insertAfter == HEAD), the `updateBlock` input should be before every other middleware's latest updateBlock. +// } else { +// /** +// * Check that `updateBlock` is before any other middleware's latest updateBlock. +// * If not, use the fallback routine to find the correct value for `insertAfter`. +// */ +// if ( +// _whitelistedContractDetails[operator][_uintToAddress( +// _operatorToWhitelistedContractsByUpdate[operator].getHead() +// )].latestUpdateBlock <= updateBlock +// ) { +// runFallbackRoutine = true; +// } +// // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct on all counts +// if (!runFallbackRoutine) { +// /** +// * Insert the middleware at the start (i.e. HEAD) of the list. +// * This will fail if `msg.sender` is already in the list, which they shouldn't be because they were removed from the list above. +// */ +// require( +// _operatorToWhitelistedContractsByUpdate[operator].pushFront(_addressToUint(msg.sender)), +// "Slasher.recordStakeUpdate: Preppending middleware unsuccessful" +// ); +// // in this case (runFallbackRoutine == true), we run a search routine to find the correct input value of `insertAfter` and then rerun this function +// } else { +// insertAfter = getCorrectValueForInsertAfter(operator, updateBlock); +// _updateMiddlewareList(operator, updateBlock, insertAfter); +// } +// } +// } + +// function _addressToUint(address addr) internal pure returns (uint256) { +// return uint256(uint160(addr)); +// } + +// function _uintToAddress(uint256 x) internal pure returns (address) { +// return address(uint160(x)); +// } + +// /** +// * @dev This empty reserved space is put in place to allow future versions to add new +// * variables without shifting down storage in the inheritance chain. +// * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +// */ +// uint256[46] private __gap; +// } diff --git a/mainnet-contracts/test/mocks/stETHStrategyMock.sol b/mainnet-contracts/test/mocks/stETHStrategyMock.sol index 77284c7a..9a55fc59 100644 --- a/mainnet-contracts/test/mocks/stETHStrategyMock.sol +++ b/mainnet-contracts/test/mocks/stETHStrategyMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IStrategy } from "../../src/interface/EigenLayer/IStrategy.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; contract stETHStrategyMock is IStrategy { /** diff --git a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol b/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol index 08183f3d..a86f4497 100644 --- a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol +++ b/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IStrategy } from "../../src/interface/EigenLayer/IStrategy.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; contract stETHStrategyTestnet is IStrategy { /** diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index a1cbdabe..95fc921e 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -9,12 +9,12 @@ import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.s import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { Merkle } from "murky/Merkle.sol"; -import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; +import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { Unauthorized } from "../../src/Errors.sol"; import { ROLE_ID_OPERATIONS_PAYMASTER } from "../../script/Roles.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; -import { IRestakingOperator } from "../../src/interface/IRestakingOperator.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { RestakingOperator } from "src/RestakingOperator.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; contract PufferModuleUpgrade { @@ -144,7 +144,7 @@ contract PufferModuleManagerTest is UnitTestHelper { vm.assume(claimer != address(0)); vm.startPrank(DAO); - IRestakingOperator operator = _createRestakingOperator(); + RestakingOperator operator = _createRestakingOperator(); vm.expectEmit(true, true, true, true); emit IPufferModuleManager.ClaimerSet({ rewardsReceiver: address(operator), claimer: claimer }); @@ -214,7 +214,7 @@ contract PufferModuleManagerTest is UnitTestHelper { address createdModule = _createPufferModule(moduleName); vm.startPrank(DAO); - IRestakingOperator operator = _createRestakingOperator(); + RestakingOperator operator = _createRestakingOperator(); vm.expectEmit(true, true, true, true); emit IPufferModuleManager.ClaimerSet(address(createdModule), claimer); @@ -245,7 +245,7 @@ contract PufferModuleManagerTest is UnitTestHelper { vm.startPrank(DAO); - IRestakingOperator operator = _createRestakingOperator(); + RestakingOperator operator = _createRestakingOperator(); bytes32 salt = 0xdebc2c61283b511dc62175c508bc9c6ad8ca754ba918164e6a9b19765c98006d; bytes32 digestHash = keccak256( @@ -276,7 +276,7 @@ contract PufferModuleManagerTest is UnitTestHelper { function test_customExternalCall() public { vm.startPrank(DAO); - IRestakingOperator operator = _createRestakingOperator(); + RestakingOperator operator = _createRestakingOperator(); bytes memory customCalldata = abi.encodeCall(PufferModuleManagerTest.getMagicNumber, ()); @@ -360,8 +360,8 @@ contract PufferModuleManagerTest is UnitTestHelper { root = rewardsMerkleProof.getRoot(rewardsMerkleProofData); } - function _createRestakingOperator() internal returns (IRestakingOperator) { - IRestakingOperator operator = pufferModuleManager.createNewRestakingOperator({ + function _createRestakingOperator() internal returns (RestakingOperator) { + RestakingOperator operator = pufferModuleManager.createNewRestakingOperator({ metadataURI: "https://puffer.fi/metadata.json", delegationApprover: address(0), stakerOptOutWindowBlocks: 0 diff --git a/yarn.lock b/yarn.lock index a6e9a338..3b33d90b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -522,12 +522,6 @@ domutils@^3.0.1: dependencies: solidity-docgen "^0.6.0-beta.32" -"eigenlayer-contracts@https://github.com/Layr-Labs/eigenlayer-contracts.git#4478eb6": - version "1.0.0" - resolved "https://github.com/Layr-Labs/eigenlayer-contracts.git#4478eb6eff809c249921adb6f3af372dac3978f8" - dependencies: - solidity-docgen "^0.6.0-beta.32" - "eigenlayer-middleware@https://github.com/bxmmm1/eigenlayer-middleware.git#dbf6c1a": version "1.0.0" resolved "https://github.com/bxmmm1/eigenlayer-middleware.git#dbf6c1a8d2be12cea9fe07210566ca631ac9d711" From 5e06bf7a7001c659eacc0116cfc200190ffe4145 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 12 Dec 2024 13:39:21 +0100 Subject: [PATCH 03/37] eeeeveerrryyythiing wip --- .../script/CompleteQueuedWithdrawals.s.sol | 6 +- mainnet-contracts/src/PufferModuleManager.sol | 58 ++----- mainnet-contracts/src/PufferProtocol.sol | 6 +- mainnet-contracts/src/RestakingOperator.sol | 4 +- .../src/interface/IPufferModuleManager.sol | 145 ------------------ 5 files changed, 21 insertions(+), 198 deletions(-) diff --git a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol index c8afbc06..d1d677e9 100644 --- a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol +++ b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol @@ -6,6 +6,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; struct ScriptParameters { address pufferModuleManager; @@ -32,7 +33,8 @@ contract CompleteQueuedWithdrawals is Script { address public BEACON_CHAIN_STRATEGY = 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0; function run(ScriptParameters memory params) external { - IDelegationManager.Withdrawal[] memory withdrawals = new IDelegationManager.Withdrawal[](params.nonces.length); + IDelegationManagerTypes.Withdrawal[] memory withdrawals = + new IDelegationManagerTypes.Withdrawal[](params.nonces.length); // Withdrawal data can be fetched from the transaction logs, for example: // cast run 0x3fecf92f659089b796922a11271e713bc97040f1a21b2671274577d4b294c5b9 --rpc-url=$HOLESKY_RPC_URL --verbose @@ -45,7 +47,7 @@ contract CompleteQueuedWithdrawals is Script { IStrategy[] memory strategies = new IStrategy[](1); strategies[0] = IStrategy(BEACON_CHAIN_STRATEGY); - withdrawals[i] = IDelegationManager.Withdrawal({ + withdrawals[i] = IDelegationManagerTypes.Withdrawal({ staker: params.pufferModule, delegatedTo: params.delegatedTo[i], withdrawer: params.pufferModule, diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 2c48bf81..819f3545 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -28,29 +28,11 @@ import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAlloca * @custom:security-contact security@puffer.fi */ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, UUPSUpgradeable { - /** - * @inheritdoc IPufferModuleManager - */ - address public immutable override PUFFER_MODULE_BEACON; - - /** - * @inheritdoc IPufferModuleManager - */ - address public immutable override RESTAKING_OPERATOR_BEACON; - - /** - * @inheritdoc IPufferModuleManager - */ - address public immutable override PUFFER_PROTOCOL; - - /** - * @inheritdoc IPufferModuleManager - */ - address payable public immutable override PUFFER_VAULT; + address public immutable PUFFER_MODULE_BEACON; + address public immutable RESTAKING_OPERATOR_BEACON; + address public immutable PUFFER_PROTOCOL; + address payable public immutable PUFFER_VAULT; - /** - * @dev AVS contracts registry - */ AVSContractsRegistry public immutable AVS_CONTRACTS_REGISTRY; modifier onlyPufferProtocol() { @@ -91,7 +73,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, bytes32 moduleName, IDelegationManagerTypes.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens ) external virtual restricted { address moduleAddress = IPufferProtocol(PUFFER_PROTOCOL).getModuleAddress(moduleName); @@ -99,7 +80,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, IPufferModule(moduleAddress).completeQueuedWithdrawals({ withdrawals: withdrawals, tokens: tokens, - middlewareTimesIndexes: middlewareTimesIndexes, receiveAsTokens: receiveAsTokens }); @@ -222,12 +202,12 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ - function callModifyOperatorDetails( - RestakingOperator restakingOperator, - address newOperatorDetails, - address newDelegationApprover - ) external virtual restricted { - restakingOperator.modifyOperatorDetails(newOperatorDetails, newDelegationApprover); + function callModifyOperatorDetails(RestakingOperator restakingOperator, address newDelegationApprover) + external + virtual + restricted + { + restakingOperator.modifyOperatorDetails(newDelegationApprover); emit RestakingOperatorModified(address(restakingOperator), newDelegationApprover); } @@ -244,15 +224,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, emit RestakingOperatorMetadataURIUpdated(address(restakingOperator), metadataURI); } - /** - * @inheritdoc IPufferModuleManager - * @dev Restricted to the DAO - */ - function callOptIntoSlashing(RestakingOperator restakingOperator, address slasher) external virtual restricted { - restakingOperator.optIntoSlashing(slasher); - emit RestakingOperatorOptedInSlasher(address(restakingOperator), slasher); - } - /** * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO @@ -290,8 +261,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, IAllocationManager.RegisterParams calldata registrationParams ) external virtual restricted { restakingOperator.registerOperatorToAVS(registrationParams); - - emit RestakingOperatorRegisteredToAVS(address(restakingOperator), avsRegistryCoordinator, quorumNumbers, socket); } /** @@ -322,12 +291,9 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, */ function callDeregisterOperatorFromAVS( RestakingOperator restakingOperator, - address avsRegistryCoordinator, - bytes calldata quorumNumbers + IAllocationManager.DeregisterParams calldata deregistrationParams ) external virtual restricted { - restakingOperator.deregisterOperatorFromAVS(avsRegistryCoordinator, quorumNumbers); - - emit RestakingOperatorDeregisteredFromAVS(restakingOperator, avsRegistryCoordinator, quorumNumbers); + restakingOperator.deregisterOperatorFromAVS(deregistrationParams); } /** diff --git a/mainnet-contracts/src/PufferProtocol.sol b/mainnet-contracts/src/PufferProtocol.sol index 34abea19..81321114 100644 --- a/mainnet-contracts/src/PufferProtocol.sol +++ b/mainnet-contracts/src/PufferProtocol.sol @@ -6,7 +6,7 @@ import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { PufferProtocolStorage } from "./PufferProtocolStorage.sol"; -import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; +import { PufferModuleManager } from "./PufferModuleManager.sol"; import { IPufferOracleV2 } from "./interface/IPufferOracleV2.sol"; import { IGuardianModule } from "./interface/IGuardianModule.sol"; import { IBeaconDepositContract } from "./interface/IBeaconDepositContract.sol"; @@ -88,7 +88,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad /** * @inheritdoc IPufferProtocol */ - IPufferModuleManager public immutable override PUFFER_MODULE_MANAGER; + PufferModuleManager public immutable override PUFFER_MODULE_MANAGER; /** * @inheritdoc IPufferProtocol @@ -110,7 +110,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad ) { GUARDIAN_MODULE = guardianModule; PUFFER_VAULT = PufferVaultV2(payable(address(pufferVault))); - PUFFER_MODULE_MANAGER = IPufferModuleManager(moduleManager); + PUFFER_MODULE_MANAGER = PufferModuleManager(moduleManager); VALIDATOR_TICKET = validatorTicket; PUFFER_ORACLE = oracle; BEACON_DEPOSIT_CONTRACT = IBeaconDepositContract(beaconDepositContract); diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index a902b649..b37394e4 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -55,12 +55,12 @@ contract RestakingOperator is IERC1271, Initializable, AccessManagedUpgradeable /** * @dev Upgradeable contract from EigenLayer */ - IDelegationManager public immutable override EIGEN_DELEGATION_MANAGER; + IDelegationManager public immutable EIGEN_DELEGATION_MANAGER; /** * @dev Upgradeable contract from EigenLayer */ - IAllocationManager public immutable override EIGEN_ALLOCATION_MANAGER; + IAllocationManager public immutable EIGEN_ALLOCATION_MANAGER; /** * @dev Upgradeable Puffer Module Manager diff --git a/mainnet-contracts/src/interface/IPufferModuleManager.sol b/mainnet-contracts/src/interface/IPufferModuleManager.sol index c2711005..6e077e84 100644 --- a/mainnet-contracts/src/interface/IPufferModuleManager.sol +++ b/mainnet-contracts/src/interface/IPufferModuleManager.sol @@ -1,11 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IPufferModule } from "../interface/IPufferModule.sol"; -import { IDelegationManager } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; import { RestakingOperator } from "src/RestakingOperator.sol"; /** * @title IPufferModuleManager @@ -118,144 +113,4 @@ interface IPufferModuleManager { * @dev Signature "0x4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a28" */ event ClaimerSet(address indexed rewardsReceiver, address indexed claimer); - - /** - * @notice Returns the Puffer Module beacon address - */ - function PUFFER_MODULE_BEACON() external view returns (address); - - /** - * @notice Returns the Restaking Operator beacon address - */ - function RESTAKING_OPERATOR_BEACON() external view returns (address); - - /** - * @notice Returns the Puffer Protocol address - */ - function PUFFER_PROTOCOL() external view returns (address); - - /** - * @notice Returns the Puffer Vault address - */ - function PUFFER_VAULT() external view returns (address payable); - - /** - * @notice Create a new Restaking Operator - * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator. - * - * @param delegationApprover Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations". - * - * @dev See IDelegationManager(EigenLayer) for more details about the other parameters - * @dev Signature verification follows these rules: - * 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed. - * 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator. - * 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value". - * @return module The newly created Puffer module - */ - function createNewRestakingOperator( - string memory metadataURI, - address delegationApprover, - uint32 stakerOptOutWindowBlocks - ) external returns (RestakingOperator module); - - /** - * @notice Create a new Puffer module - * @dev This function creates a new Puffer module with the given module name - * @param moduleName The name of the module - * @return module The newly created Puffer module - */ - function createNewPufferModule(bytes32 moduleName) external returns (IPufferModule module); - - /** - * @notice Sets proof Submitter on the Puffer Module - * @param moduleName The name of the module - * @param proofSubmitter The address of the proof submitter - */ - function callSetProofSubmitter(bytes32 moduleName, address proofSubmitter) external; - - /** - * @notice Starts the checkpointing on puffer modules - */ - function callStartCheckpoint(address[] calldata moduleAddresses) external; - - /** - * @notice Calls the modifyOperatorDetails function on the restaking operator - * @param restakingOperator is the address of the restaking operator - * @dev See IDelegationManager(EigenLayer) for more details about the other parameters - * @dev Restricted to the DAO - */ - function callModifyOperatorDetails( - RestakingOperator restakingOperator, - IDelegationManager.OperatorDetails calldata newOperatorDetails - ) external; - - /** - * @notice Calls `queueWithdrawals` from the PufferModule `moduleName` - * @param moduleName is the name of the module - * @param sharesAmount is the amount of shares to withdraw - */ - function callQueueWithdrawals(bytes32 moduleName, uint256 sharesAmount) external; - - /** - * @notice Calls `completeQueuedWithdrawals` from the PufferModule `moduleName` - * @dev See IDelegationManager(EigenLayer) for more details about the other parameters - */ - function callCompleteQueuedWithdrawals( - bytes32 moduleName, - IDelegationManager.Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, - bool[] calldata receiveAsTokens - ) external; - - /** - * @notice Calls the optIntoSlashing function on the restaking operator - * @param restakingOperator is the address of the restaking operator - * @param slasher is the address of the slasher contract to opt into - * @dev Restricted to the DAO - */ - function callOptIntoSlashing(RestakingOperator restakingOperator, address slasher) external; - - /** - * @notice Calls the updateOperatorMetadataURI function on the restaking operator - * @param restakingOperator is the address of the restaking operator - * @param metadataURI is the URI of the operator's metadata - * @dev Restricted to the DAO - */ - function callUpdateMetadataURI(RestakingOperator restakingOperator, string calldata metadataURI) external; - - /** - * @notice Calls the callDelegateTo function on the target module - * @param moduleName is the name of the module - * @param operator is the address of the restaking operator - * @param approverSignatureAndExpiry the signature of the delegation approver - * @param approverSalt salt for the signature - * @dev Restricted to the DAO - */ - function callDelegateTo( - bytes32 moduleName, - address operator, - ISignatureUtils.SignatureWithExpiry calldata approverSignatureAndExpiry, - bytes32 approverSalt - ) external; - - /** - * @notice Calls the callUndelegate function on the target module - * @param moduleName is the name of the module - * @dev Restricted to the DAO - */ - function callUndelegate(bytes32 moduleName) external returns (bytes32[] memory withdrawalRoot); - - /** - * @notice Updates AVS registration signature proof - * @param restakingOperator is the address of the restaking operator - * @param digestHash is the message hash - * @param signer is the address of the signature signer - * @dev Restricted to the DAO - */ - function updateAVSRegistrationSignatureProof( - RestakingOperator restakingOperator, - bytes32 digestHash, - address signer - ) external; } From 78d663167cc8916f68c3984732786c5d3547afbc Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 12 Dec 2024 13:50:24 +0100 Subject: [PATCH 04/37] more --- .../script/CompleteQueuedWithdrawals.s.sol | 2 +- .../script/DeployRestakingOperator.s.sol | 6 ++-- .../script/GenerateAccessManagerCallData.sol | 3 +- mainnet-contracts/script/SetupAccess.s.sol | 19 ++++++------- .../script/UpgradePufferModule.s.sol | 4 +-- mainnet-contracts/src/PufferProtocol.sol | 4 +-- mainnet-contracts/src/RestakingOperator.sol | 3 -- .../src/interface/IPufferProtocol.sol | 28 ++----------------- .../PufferModuleManager.integration.t.sol | 26 +++++------------ .../test/mocks/DelegationManagerMock.sol | 12 ++------ 10 files changed, 29 insertions(+), 78 deletions(-) diff --git a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol index d1d677e9..6736d5f5 100644 --- a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol +++ b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol @@ -54,7 +54,7 @@ contract CompleteQueuedWithdrawals is Script { nonce: params.nonces[i], startBlock: params.startBlocks[i], strategies: strategies, - shares: shares + scaledShares: shares }); } diff --git a/mainnet-contracts/script/DeployRestakingOperator.s.sol b/mainnet-contracts/script/DeployRestakingOperator.s.sol index cee7411e..b53f44a3 100644 --- a/mainnet-contracts/script/DeployRestakingOperator.s.sol +++ b/mainnet-contracts/script/DeployRestakingOperator.s.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Script.sol"; -import { IPufferModuleManager } from "../src/interface/IPufferModuleManager.sol"; +import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { RestakingOperator } from "../src/RestakingOperator.sol"; @@ -24,8 +24,8 @@ contract DeployRestakingOperator is DeployerHelper { RestakingOperator restakingOperatorImplementation = new RestakingOperator({ delegationManager: IDelegationManager(_getEigenDelegationManager()), - slasher: IAllocationManager(_getEigenSlasher()), - moduleManager: IPufferModuleManager(_getPufferModuleManager()), + allocationManager: IAllocationManager(_getEigenSlasher()), + moduleManager: PufferModuleManager(payable(_getPufferModuleManager())), rewardsCoordinator: IRewardsCoordinator(_getRewardsCoordinator()) }); diff --git a/mainnet-contracts/script/GenerateAccessManagerCallData.sol b/mainnet-contracts/script/GenerateAccessManagerCallData.sol index bbd272f0..6a87ff35 100644 --- a/mainnet-contracts/script/GenerateAccessManagerCallData.sol +++ b/mainnet-contracts/script/GenerateAccessManagerCallData.sol @@ -76,10 +76,9 @@ contract GenerateAccessManagerCallData is Script { function _getOperationsSelectorsCalldata(address pufferVaultProxy) internal pure returns (bytes memory) { // Operations multisig - bytes4[] memory operationsSelectors = new bytes4[](3); + bytes4[] memory operationsSelectors = new bytes4[](2); operationsSelectors[0] = PufferVaultV2.initiateETHWithdrawalsFromLido.selector; operationsSelectors[1] = PufferVaultV2.claimWithdrawalsFromLido.selector; - operationsSelectors[2] = PufferVaultV2.claimWithdrawalFromEigenLayerM2.selector; return abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, diff --git a/mainnet-contracts/script/SetupAccess.s.sol b/mainnet-contracts/script/SetupAccess.s.sol index bfc28b1a..d93abb53 100644 --- a/mainnet-contracts/script/SetupAccess.s.sol +++ b/mainnet-contracts/script/SetupAccess.s.sol @@ -150,19 +150,16 @@ contract SetupAccess is BaseScript { bytes[] memory calldatas = new bytes[](2); // Dao selectors - bytes4[] memory selectors = new bytes4[](12); + bytes4[] memory selectors = new bytes4[](9); selectors[0] = PufferModuleManager.createNewRestakingOperator.selector; selectors[1] = PufferModuleManager.callModifyOperatorDetails.selector; - selectors[2] = PufferModuleManager.callOptIntoSlashing.selector; - selectors[3] = PufferModuleManager.callUpdateMetadataURI.selector; - selectors[4] = PufferModuleManager.callUndelegate.selector; - selectors[5] = PufferModuleManager.callDelegateTo.selector; - selectors[6] = PufferModuleManager.updateAVSRegistrationSignatureProof.selector; - selectors[7] = PufferModuleManager.callRegisterOperatorToAVS.selector; - selectors[8] = PufferModuleManager.callRegisterOperatorToAVSWithChurn.selector; - selectors[9] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; - selectors[10] = PufferModuleManager.callUpdateOperatorAVSSocket.selector; - selectors[11] = PufferModuleManager.customExternalCall.selector; + selectors[2] = PufferModuleManager.callUpdateMetadataURI.selector; + selectors[3] = PufferModuleManager.callUndelegate.selector; + selectors[4] = PufferModuleManager.callDelegateTo.selector; + selectors[5] = PufferModuleManager.updateAVSRegistrationSignatureProof.selector; + selectors[6] = PufferModuleManager.callRegisterOperatorToAVS.selector; + selectors[7] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; + selectors[8] = PufferModuleManager.customExternalCall.selector; calldatas[0] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, pufferDeployment.moduleManager, selectors, ROLE_ID_DAO diff --git a/mainnet-contracts/script/UpgradePufferModule.s.sol b/mainnet-contracts/script/UpgradePufferModule.s.sol index ba995e17..d346af8f 100644 --- a/mainnet-contracts/script/UpgradePufferModule.s.sol +++ b/mainnet-contracts/script/UpgradePufferModule.s.sol @@ -11,7 +11,7 @@ import { stdJson } from "forge-std/StdJson.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { PufferModule } from "../src/PufferModule.sol"; import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; /** * @title UpgradePufferModule @@ -44,7 +44,7 @@ contract UpgradePufferModule is BaseScript { eigenPodManager: eigenPodManager, delegationManager: delegationManager, moduleManager: pufferModuleManager, - rewardsCoordinator: rewardsCoordinator + rewardsCoordinator: IRewardsCoordinator(rewardsCoordinator) }); console.log("newImplementation", address(newImplementation)); diff --git a/mainnet-contracts/src/PufferProtocol.sol b/mainnet-contracts/src/PufferProtocol.sol index 81321114..053d3655 100644 --- a/mainnet-contracts/src/PufferProtocol.sol +++ b/mainnet-contracts/src/PufferProtocol.sol @@ -88,7 +88,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad /** * @inheritdoc IPufferProtocol */ - PufferModuleManager public immutable override PUFFER_MODULE_MANAGER; + PufferModuleManager public immutable PUFFER_MODULE_MANAGER; /** * @inheritdoc IPufferProtocol @@ -110,7 +110,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad ) { GUARDIAN_MODULE = guardianModule; PUFFER_VAULT = PufferVaultV2(payable(address(pufferVault))); - PUFFER_MODULE_MANAGER = PufferModuleManager(moduleManager); + PUFFER_MODULE_MANAGER = PufferModuleManager(payable(moduleManager)); VALIDATOR_TICKET = validatorTicket; PUFFER_ORACLE = oracle; BEACON_DEPOSIT_CONTRACT = IBeaconDepositContract(beaconDepositContract); diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index b37394e4..e1211616 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -11,9 +11,6 @@ import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { IRegistryCoordinatorExtended } from "./interface/IRegistryCoordinatorExtended.sol"; -import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IRewardsCoordinator } from "./interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; /** diff --git a/mainnet-contracts/src/interface/IPufferProtocol.sol b/mainnet-contracts/src/interface/IPufferProtocol.sol index b17a48f0..95b61781 100644 --- a/mainnet-contracts/src/interface/IPufferProtocol.sol +++ b/mainnet-contracts/src/interface/IPufferProtocol.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.0 <0.9.0; import { Validator } from "../struct/Validator.sol"; import { ValidatorKeyData } from "../struct/ValidatorKeyData.sol"; import { IGuardianModule } from "../interface/IGuardianModule.sol"; -import { IPufferModuleManager } from "../interface/IPufferModuleManager.sol"; +import { PufferModuleManager } from "../PufferModuleManager.sol"; import { PufferVaultV2 } from "../PufferVaultV2.sol"; import { IPufferOracleV2 } from "../interface/IPufferOracleV2.sol"; import { Status } from "../struct/Status.sol"; @@ -232,30 +232,6 @@ interface IPufferProtocol { */ function skipProvisioning(bytes32 moduleName, bytes[] calldata guardianEOASignatures) external; - /** - * @notice Sets the module weights array to `newModuleWeights` - * @dev Restricted to the DAO - */ - function setModuleWeights(bytes32[] calldata newModuleWeights) external; - - /** - * @notice Sets the module limits for `moduleName` to `limit` - * @dev Restricted to the DAO - */ - function setValidatorLimitPerModule(bytes32 moduleName, uint128 limit) external; - - /** - * @notice Sets the Validator Ticket penalty amount to `newPenaltyAmount` - * @dev Restricted to the DAO - */ - function setVTPenalty(uint256 newPenaltyAmount) external; - - /** - * @notice Changes the minimum number amount of VT that must be locked per validator - * @dev Restricted to the DAO - */ - function changeMinimumVTAmount(uint256 newMinimumVTAmount) external; - /** * @notice Returns the guardian module */ @@ -274,7 +250,7 @@ interface IPufferProtocol { /** * @notice Returns the Puffer Module Manager */ - function PUFFER_MODULE_MANAGER() external view returns (IPufferModuleManager); + function PUFFER_MODULE_MANAGER() external view returns (PufferModuleManager); /** * @notice Returns the Puffer Oracle diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index f36823db..fb59d987 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -44,34 +44,22 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { pufferProtocol.createPufferModule(bytes32("SOME_MODULE_NAME")); } - function test_opt_into_slashing() public { - vm.startPrank(DAO); - RestakingOperator operator = _createRestakingOperator(); - - address slasher = address(1235); - - vm.expectEmit(true, true, true, true); - emit IPufferModuleManager.RestakingOperatorOptedInSlasher(address(operator), slasher); - moduleManager.callOptIntoSlashing(operator, slasher); - } - function test_modify_operator() public { vm.startPrank(DAO); RestakingOperator operator = _createRestakingOperator(); - IDelegationManager.OperatorDetails memory newOperatorDetails = IDelegationManager.OperatorDetails({ - __deprecated_earningsReceiver: address(this), - delegationApprover: address(0), - stakerOptOutWindowBlocks: 100 - }); + address newDelegationApprover = makeAddr("newDelegationApprover"); vm.expectEmit(true, true, true, true); - emit IPufferModuleManager.RestakingOperatorModified(address(operator), newOperatorDetails); - moduleManager.callModifyOperatorDetails({ restakingOperator: operator, newOperatorDetails: newOperatorDetails }); + emit IPufferModuleManager.RestakingOperatorModified(address(operator), newDelegationApprover); + moduleManager.callModifyOperatorDetails({ + restakingOperator: operator, + newDelegationApprover: newDelegationApprover + }); IDelegationManager.OperatorDetails memory details = operator.EIGEN_DELEGATION_MANAGER().operatorDetails(address(operator)); - assertEq(details.stakerOptOutWindowBlocks, 100, "updated blocks"); + assertEq(details.delegationApprover, newDelegationApprover, "updated delegation approver"); assertEq(details.__deprecated_earningsReceiver, address(this), "updated earnings"); } diff --git a/mainnet-contracts/test/mocks/DelegationManagerMock.sol b/mainnet-contracts/test/mocks/DelegationManagerMock.sol index b90da079..cc95b1ea 100644 --- a/mainnet-contracts/test/mocks/DelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/DelegationManagerMock.sol @@ -202,15 +202,11 @@ contract DelegationManagerMock { IERC20 token, IStrategy strategy, uint256 shares - ) external { - strategyManager.addShares(staker, token, strategy, shares); - } + ) external { } function removeShares(IStrategyManager strategyManager, address staker, IStrategy strategy, uint256 shares) external - { - strategyManager.removeShares(staker, strategy, shares); - } + { } function withdrawSharesAsTokens( IStrategyManager strategyManager, @@ -218,9 +214,7 @@ contract DelegationManagerMock { IStrategy strategy, uint256 shares, IERC20 token - ) external { - strategyManager.withdrawSharesAsTokens(recipient, strategy, shares, token); - } + ) external { } function operatorDetails(address operator) external view returns (IDelegationManager.OperatorDetails memory) { } } From 5fb8bc34342118a35d4b51d5d992d14eab50a16a Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 12 Dec 2024 13:52:50 +0100 Subject: [PATCH 05/37] filename update --- .../script/CompleteQueuedWithdrawals.s.sol | 7 +++---- mainnet-contracts/script/DeployPufETH.s.sol | 4 ++-- mainnet-contracts/script/DeployPuffer.s.sol | 6 +++--- .../script/DeployPufferModuleImplementation.s.sol | 4 ++-- mainnet-contracts/script/DeployPufferVaultV3.s.sol | 6 +++--- mainnet-contracts/script/DeployRestakingOperator.s.sol | 6 +++--- mainnet-contracts/script/UpgradePufETH.s.sol | 6 +++--- mainnet-contracts/script/UpgradePufferModule.s.sol | 4 ++-- mainnet-contracts/src/PufferModule.sol | 8 ++++---- mainnet-contracts/src/PufferModuleManager.sol | 8 ++++---- mainnet-contracts/src/PufferVault.sol | 4 ++-- mainnet-contracts/src/PufferVaultV2.sol | 6 +++--- mainnet-contracts/src/PufferVaultV3.sol | 6 +++--- mainnet-contracts/src/PufferVaultV4.sol | 6 +++--- mainnet-contracts/src/RestakingOperator.sol | 6 +++--- mainnet-contracts/src/interface/IPufferModule.sol | 8 ++++---- .../src/interface/IRegistryCoordinatorExtended.sol | 2 +- .../test/Integration/PufferRevenueDepositor.fork.t.sol | 6 +++--- .../test/Integration/PufferTest.integration.t.sol | 8 ++++---- .../Integration/PufferVaultV2WithdrawFromEl.fork.t.sol | 6 +++--- mainnet-contracts/test/MainnetForkTestHelper.sol | 6 +++--- .../fork-tests/PufferModuleManager.integration.t.sol | 10 +++++----- ...erModuleManagerHoleskyTestnetTest.integration.t.sol | 10 +++++----- .../fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol | 8 ++++---- mainnet-contracts/test/mocks/DelegationManagerMock.sol | 8 ++++---- .../test/mocks/EigenLayerDelegationManagerMock.sol | 4 ++-- mainnet-contracts/test/mocks/EigenLayerManagerMock.sol | 4 ++-- mainnet-contracts/test/mocks/EigenPodManagerMock.sol | 4 ++-- mainnet-contracts/test/mocks/PausableMock.sol | 2 +- mainnet-contracts/test/mocks/PufferVaultV2Tests.sol | 6 +++--- mainnet-contracts/test/mocks/PufferVaultV4Tests.sol | 6 +++--- mainnet-contracts/test/mocks/SlasherMock.sol | 6 +++--- mainnet-contracts/test/mocks/stETHStrategyMock.sol | 2 +- mainnet-contracts/test/mocks/stETHStrategyTestnet.sol | 2 +- mainnet-contracts/test/unit/PufferModuleManager.t.sol | 4 ++-- 35 files changed, 99 insertions(+), 100 deletions(-) diff --git a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol index 6736d5f5..717b83a9 100644 --- a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol +++ b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { Script } from "forge-std/Script.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; -import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; struct ScriptParameters { address pufferModuleManager; @@ -70,7 +70,6 @@ contract CompleteQueuedWithdrawals is Script { moduleName: params.moduleName, withdrawals: withdrawals, tokens: tokens, - middlewareTimesIndexes: middlewareTimesIndexes, receiveAsTokens: params.receiveAsTokens }); } diff --git a/mainnet-contracts/script/DeployPufETH.s.sol b/mainnet-contracts/script/DeployPufETH.s.sol index 996231aa..60fe5c56 100644 --- a/mainnet-contracts/script/DeployPufETH.s.sol +++ b/mainnet-contracts/script/DeployPufETH.s.sol @@ -10,8 +10,8 @@ import { PufferVault } from "../src/PufferVault.sol"; import { Timelock } from "../src/Timelock.sol"; import { NoImplementation } from "../src/NoImplementation.sol"; import { PufferDeployment } from "../src/structs/PufferDeployment.sol"; -import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { stETHMock } from "../test/mocks/stETHMock.sol"; diff --git a/mainnet-contracts/script/DeployPuffer.s.sol b/mainnet-contracts/script/DeployPuffer.s.sol index 851550af..7702e8b7 100644 --- a/mainnet-contracts/script/DeployPuffer.s.sol +++ b/mainnet-contracts/script/DeployPuffer.s.sol @@ -13,8 +13,8 @@ import { stdJson } from "forge-std/StdJson.sol"; import { EigenPodManagerMock } from "../test/mocks/EigenPodManagerMock.sol"; import { DelegationManagerMock } from "../test/mocks/DelegationManagerMock.sol"; import { BeaconMock } from "../test/mocks/BeaconMock.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; @@ -24,7 +24,7 @@ import { ValidatorTicketPricer } from "../src/ValidatorTicketPricer.sol"; import { OperationsCoordinator } from "../src/OperationsCoordinator.sol"; import { PufferOracleV2 } from "../src/PufferOracleV2.sol"; import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol"; -import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; import { AVSContractsRegistry } from "../src/AVSContractsRegistry.sol"; import { RewardsCoordinatorMock } from "../test/mocks/RewardsCoordinatorMock.sol"; diff --git a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol index 98c1c564..05395e88 100644 --- a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol +++ b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol @@ -12,9 +12,9 @@ import { stdJson } from "forge-std/StdJson.sol"; import { GuardianModule } from "../src/GuardianModule.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { PufferModule } from "../src/PufferModule.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; /** diff --git a/mainnet-contracts/script/DeployPufferVaultV3.s.sol b/mainnet-contracts/script/DeployPufferVaultV3.s.sol index 9df636e1..4878f140 100644 --- a/mainnet-contracts/script/DeployPufferVaultV3.s.sol +++ b/mainnet-contracts/script/DeployPufferVaultV3.s.sol @@ -9,10 +9,10 @@ import { PufferVaultV3 } from "src/PufferVaultV3.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; -import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "src/interface/EigenLayer-Slashing/IEigenLayer.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; /** * @title DeployPufferVaultV3 diff --git a/mainnet-contracts/script/DeployRestakingOperator.s.sol b/mainnet-contracts/script/DeployRestakingOperator.s.sol index b53f44a3..83d51286 100644 --- a/mainnet-contracts/script/DeployRestakingOperator.s.sol +++ b/mainnet-contracts/script/DeployRestakingOperator.s.sol @@ -6,10 +6,10 @@ import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { RestakingOperator } from "../src/RestakingOperator.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; /** diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index a77742d6..a5f47cd2 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -7,9 +7,9 @@ import { PufferVault } from "../src/PufferVault.sol"; import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; -import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { LidoWithdrawalQueueMock } from "../test/mocks/LidoWithdrawalQueueMock.sol"; diff --git a/mainnet-contracts/script/UpgradePufferModule.s.sol b/mainnet-contracts/script/UpgradePufferModule.s.sol index d346af8f..c34c59f9 100644 --- a/mainnet-contracts/script/UpgradePufferModule.s.sol +++ b/mainnet-contracts/script/UpgradePufferModule.s.sol @@ -10,8 +10,8 @@ import { BaseScript } from "script/BaseScript.s.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { PufferModule } from "../src/PufferModule.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IRewardsCoordinator } from "src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; /** * @title UpgradePufferModule diff --git a/mainnet-contracts/src/PufferModule.sol b/mainnet-contracts/src/PufferModule.sol index 4e968777..e2d9232d 100644 --- a/mainnet-contracts/src/PufferModule.sol +++ b/mainnet-contracts/src/PufferModule.sol @@ -3,9 +3,9 @@ pragma solidity >=0.8.0 <0.9.0; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { IEigenPodManager } from "../src/interface/EigenLayer-Slashing/IEigenPodManager.sol"; -import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { ISignatureUtils } from "../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { IEigenPod } from "../src/interface/EigenLayer-Slashing/IEigenPod.sol"; @@ -16,8 +16,8 @@ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/I import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ModuleStorage } from "./struct/ModuleStorage.sol"; -import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IDelegationManagerTypes } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IRewardsCoordinator } from "src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; /** * @title PufferModule diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 819f3545..2164b49f 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -14,13 +14,13 @@ import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol"; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { AVSContractsRegistry } from "./AVSContractsRegistry.sol"; import { RestakingOperator } from "./RestakingOperator.sol"; -import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; /** * @title PufferModuleManager diff --git a/mainnet-contracts/src/PufferVault.sol b/mainnet-contracts/src/PufferVault.sol index 58809642..9f84b2f7 100644 --- a/mainnet-contracts/src/PufferVault.sol +++ b/mainnet-contracts/src/PufferVault.sol @@ -5,8 +5,8 @@ import { IPufferVault } from "./interface/IPufferVault.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; import { PufferVaultStorage } from "./PufferVaultStorage.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; diff --git a/mainnet-contracts/src/PufferVaultV2.sol b/mainnet-contracts/src/PufferVaultV2.sol index ad885b0b..32625e3a 100644 --- a/mainnet-contracts/src/PufferVaultV2.sol +++ b/mainnet-contracts/src/PufferVaultV2.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVault } from "./PufferVault.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/EigenLayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV2 } from "./interface/IPufferVaultV2.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; diff --git a/mainnet-contracts/src/PufferVaultV3.sol b/mainnet-contracts/src/PufferVaultV3.sol index db0a2fc4..04e8c3c5 100644 --- a/mainnet-contracts/src/PufferVaultV3.sol +++ b/mainnet-contracts/src/PufferVaultV3.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "./PufferVaultV2.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/EigenLayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; diff --git a/mainnet-contracts/src/PufferVaultV4.sol b/mainnet-contracts/src/PufferVaultV4.sol index 474f1f0e..3b47ef4e 100644 --- a/mainnet-contracts/src/PufferVaultV4.sol +++ b/mainnet-contracts/src/PufferVaultV4.sol @@ -3,9 +3,9 @@ pragma solidity >=0.8.0 <0.9.0; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/EigenLayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; import { PufferVaultV3 } from "./PufferVaultV3.sol"; diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index e1211616..e66a4fa1 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -4,14 +4,14 @@ pragma solidity >=0.8.0 <0.9.0; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; import { Unauthorized, InvalidAddress } from "./Errors.sol"; import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import { IRewardsCoordinator } from "./interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "./interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; /** * @title RestakingOperator diff --git a/mainnet-contracts/src/interface/IPufferModule.sol b/mainnet-contracts/src/interface/IPufferModule.sol index 4d72e9a3..00dd7297 100644 --- a/mainnet-contracts/src/interface/IPufferModule.sol +++ b/mainnet-contracts/src/interface/IPufferModule.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; -// import { BeaconChainProofs } from "../interface/Eigenlayer-Slashing/BeaconChainProofs.sol"; -import { IDelegationManager } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../interface/EigenLayer-Slashing/ISignatureUtils.sol"; +// import { BeaconChainProofs } from "../interface/EigenLayer-Slashing/BeaconChainProofs.sol"; +import { IDelegationManager } from "../interface/EigenLayer-Slashing/IDelegationManager.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IDelegationManagerTypes } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../interface/EigenLayer-Slashing/IDelegationManager.sol"; /** * @title IPufferModule diff --git a/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol b/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol index d078ab52..bebe67e8 100644 --- a/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol +++ b/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { ISignatureUtils } from "../interface/EigenLayer-Slashing/ISignatureUtils.sol"; interface IRegistryCoordinatorExtended is IRegistryCoordinator { /** diff --git a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol index f88ba029..ddd34c02 100644 --- a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol @@ -12,10 +12,10 @@ import { PufferVaultV4 } from "../../src/PufferVaultV4.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; struct AssetValue { IERC20 asset; diff --git a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol index a0d26c7c..80b42516 100644 --- a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol +++ b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol @@ -9,7 +9,7 @@ import { PufferVaultV2Tests } from "../../test/mocks/PufferVaultV2Tests.sol"; import { PufferDepositorV2 } from "../../src/PufferDepositorV2.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { MockPufferOracle } from "../mocks/MockPufferOracle.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; import { IPufferVault } from "../../src/interface/IPufferVault.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -22,13 +22,13 @@ import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessMana import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { IWstETH } from "../../src/interface/Lido/IWstETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { Timelock } from "../../src/Timelock.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; import { Permit } from "../../src/structs/Permit.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; /** * @dev PufferDepositor and PufferVault tests (v1) diff --git a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol index ef60f431..b60f4108 100644 --- a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol @@ -6,9 +6,9 @@ import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; diff --git a/mainnet-contracts/test/MainnetForkTestHelper.sol b/mainnet-contracts/test/MainnetForkTestHelper.sol index 5ea810d6..ac39ac3c 100644 --- a/mainnet-contracts/test/MainnetForkTestHelper.sol +++ b/mainnet-contracts/test/MainnetForkTestHelper.sol @@ -10,7 +10,7 @@ import { PufferVaultV2Tests } from "../test/mocks/PufferVaultV2Tests.sol"; import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; import { PufferDepositorV2 } from "../src/PufferDepositorV2.sol"; import { MockPufferOracle } from "./mocks/MockPufferOracle.sol"; -import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IEigenLayer } from "../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol"; @@ -19,13 +19,13 @@ import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { IPufferOracle } from "../src/interface/IPufferOracle.sol"; import { IWstETH } from "../src/interface/Lido/IWstETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { Timelock } from "../src/Timelock.sol"; import { IWETH } from "../src/interface/Other/IWETH.sol"; import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; import { Permit } from "../src/structs/Permit.sol"; import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { DeployerHelper } from "../script/DeployerHelper.s.sol"; import { IPufferRevenueDepositor } from "../src/interface/IPufferRevenueDepositor.sol"; diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index fb59d987..d00c43ec 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -8,12 +8,12 @@ import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.sol"; import { RestakingOperator } from "../../src/RestakingOperator.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; -import { IStrategyManager } from "../../src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { ISignatureUtils } from "../../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; +import { IStrategyManager } from "../../src/interface/EigenLayer-Slashing/IStrategyManager.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IBLSApkRegistry, IRegistryCoordinator } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAVSDirectory } from "../../src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; +import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; import { IRegistryCoordinatorExtended } from "../../src/interface/IRegistryCoordinatorExtended.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol index fb42037b..e18df738 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol @@ -11,12 +11,12 @@ import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.s import { AVSContractsRegistry } from "../../src/AVSContractsRegistry.sol"; import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; -import { IRewardsCoordinator } from "../../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { IAVSDirectory } from "../../src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; +import { IRewardsCoordinator } from "../../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol index e1255dbf..6ccd5a3b 100644 --- a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol +++ b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol @@ -7,11 +7,11 @@ import { DeployEverything } from "script/DeployEverything.s.sol"; import { IPufferModuleManager } from "src/interface/IPufferModuleManager.sol"; import { PufferModuleManager } from "src/PufferModuleManager.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; import { IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { IAVSDirectory } from "src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAVSDirectory } from "src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; +import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; import { IRegistryCoordinatorExtended } from "src/interface/IRegistryCoordinatorExtended.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; diff --git a/mainnet-contracts/test/mocks/DelegationManagerMock.sol b/mainnet-contracts/test/mocks/DelegationManagerMock.sol index cc95b1ea..fa8958a5 100644 --- a/mainnet-contracts/test/mocks/DelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/DelegationManagerMock.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; -import { IStrategyManager } from "src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; +import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { ISignatureUtils } from "src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; +import { IStrategyManager } from "src/interface/EigenLayer-Slashing/IStrategyManager.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract DelegationManagerMock { diff --git a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol index 4d3f8e6f..eda03cc9 100644 --- a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; contract EigenLayerDelegationManagerMock is IDelegationManager { /** diff --git a/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol index 0faf9f7c..99e2801f 100644 --- a/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; contract EigenLayerManagerMock is IEigenLayer { function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares) { } diff --git a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol index 580f8d52..3f0be83b 100644 --- a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Test.sol"; -import "src/interface/Eigenlayer-Slashing/IEigenPodManager.sol"; -import "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import "src/interface/EigenLayer-Slashing/IEigenPodManager.sol"; +import "src/interface/EigenLayer-Slashing/IAllocationManager.sol"; contract EigenPodMock { } diff --git a/mainnet-contracts/test/mocks/PausableMock.sol b/mainnet-contracts/test/mocks/PausableMock.sol index d8f5b17e..3adf92fb 100644 --- a/mainnet-contracts/test/mocks/PausableMock.sol +++ b/mainnet-contracts/test/mocks/PausableMock.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.12; -import "src/interface/Eigenlayer-Slashing/IPausable.sol"; +import "src/interface/EigenLayer-Slashing/IPausable.sol"; /** * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract. diff --git a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol index a23c9aff..64ba6134 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "src/PufferVaultV2.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; contract PufferVaultV2Tests is PufferVaultV2 { constructor( diff --git a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol index e949f9e8..3ab4d16f 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV4 } from "src/PufferVaultV4.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { IPufferRevenueDepositor } from "src/interface/IPufferRevenueDepositor.sol"; contract PufferVaultV4Tests is PufferVaultV4 { diff --git a/mainnet-contracts/test/mocks/SlasherMock.sol b/mainnet-contracts/test/mocks/SlasherMock.sol index 09a2b9fd..9cea506e 100644 --- a/mainnet-contracts/test/mocks/SlasherMock.sol +++ b/mainnet-contracts/test/mocks/SlasherMock.sol @@ -1,9 +1,9 @@ // // SPDX-License-Identifier: BUSL-1.1 // pragma solidity >=0.8.12; -// import "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; -// import "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -// import "src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; +// import "src/interface/EigenLayer-Slashing/IAllocationManager.sol"; +// import "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +// import "src/interface/EigenLayer-Slashing/IStrategyManager.sol"; // import "test/mocks/StructuredLinkedListMock.sol"; // import "test/mocks/PausableMock.sol"; // import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; diff --git a/mainnet-contracts/test/mocks/stETHStrategyMock.sol b/mainnet-contracts/test/mocks/stETHStrategyMock.sol index 9a55fc59..e794d4f6 100644 --- a/mainnet-contracts/test/mocks/stETHStrategyMock.sol +++ b/mainnet-contracts/test/mocks/stETHStrategyMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; contract stETHStrategyMock is IStrategy { /** diff --git a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol b/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol index a86f4497..514e7d6e 100644 --- a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol +++ b/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; contract stETHStrategyTestnet is IStrategy { /** diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index 95fc921e..efb18401 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -9,11 +9,11 @@ import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.s import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { Merkle } from "murky/Merkle.sol"; -import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { ISignatureUtils } from "src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; import { Unauthorized } from "../../src/Errors.sol"; import { ROLE_ID_OPERATIONS_PAYMASTER } from "../../script/Roles.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { RestakingOperator } from "src/RestakingOperator.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; From 9ea8d22e3c7a4aafa924497e06c8cd502fef67c4 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 12 Dec 2024 14:02:12 +0100 Subject: [PATCH 06/37] remove more code --- mainnet-contracts/src/PufferModule.sol | 6 +- mainnet-contracts/src/PufferModuleManager.sol | 8 --- mainnet-contracts/src/PufferProtocol.sol | 5 -- .../PufferModuleManager.integration.t.sol | 17 ++--- ...anagerHoleskyTestnetTest.integration.t.sol | 13 ++-- .../ffi/PufferModuleManagerHoleskyFfi.t.sol | 67 ------------------- .../test/unit/PufferModuleManager.t.sol | 11 +-- 7 files changed, 16 insertions(+), 111 deletions(-) diff --git a/mainnet-contracts/src/PufferModule.sol b/mainnet-contracts/src/PufferModule.sol index e2d9232d..eadced6d 100644 --- a/mainnet-contracts/src/PufferModule.sol +++ b/mainnet-contracts/src/PufferModule.sol @@ -9,7 +9,7 @@ import { ISignatureUtils } from "../src/interface/EigenLayer-Slashing/ISignature import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { IEigenPod } from "../src/interface/EigenLayer-Slashing/IEigenPod.sol"; -import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; +import { PufferModuleManager } from "./PufferModuleManager.sol"; import { IPufferModule } from "./interface/IPufferModule.sol"; import { Unauthorized } from "./Errors.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; @@ -62,7 +62,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable * @dev Upgradeable Puffer Module Manager * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ - IPufferModuleManager public immutable PUFFER_MODULE_MANAGER; + PufferModuleManager public immutable PUFFER_MODULE_MANAGER; /** * keccak256(abi.encode(uint256(keccak256("PufferModule.storage")) - 1)) & ~bytes32(uint256(0xff)) @@ -77,7 +77,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable IPufferProtocol protocol, address eigenPodManager, IDelegationManager delegationManager, - IPufferModuleManager moduleManager, + PufferModuleManager moduleManager, IRewardsCoordinator rewardsCoordinator ) payable { EIGEN_POD_MANAGER = IEigenPodManager(eigenPodManager); diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 2164b49f..4f9a7455 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -140,7 +140,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to Puffer Paymaster */ function callQueueWithdrawals(bytes32 moduleName, uint256 sharesAmount) external virtual restricted { @@ -150,7 +149,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callSetClaimerFor(address moduleOrReOp, address claimer) external virtual restricted { @@ -160,7 +158,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callSetProofSubmitter(bytes32 moduleName, address proofSubmitter) external virtual restricted { @@ -170,7 +167,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function createNewRestakingOperator(string calldata metadataURI, address delegationApprover, uint32 allocationDelay) @@ -199,7 +195,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callModifyOperatorDetails(RestakingOperator restakingOperator, address newDelegationApprover) @@ -212,7 +207,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callUpdateMetadataURI(RestakingOperator restakingOperator, string calldata metadataURI) @@ -225,7 +219,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callDelegateTo( @@ -242,7 +235,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @inheritdoc IPufferModuleManager * @dev Restricted to the DAO */ function callUndelegate(bytes32 moduleName) external virtual restricted returns (bytes32[] memory withdrawalRoot) { diff --git a/mainnet-contracts/src/PufferProtocol.sol b/mainnet-contracts/src/PufferProtocol.sol index 053d3655..7da96911 100644 --- a/mainnet-contracts/src/PufferProtocol.sol +++ b/mainnet-contracts/src/PufferProtocol.sol @@ -423,7 +423,6 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad } /** - * @inheritdoc IPufferProtocol * @dev Restricted to the DAO */ function changeMinimumVTAmount(uint256 newMinimumVTAmount) external restricted { @@ -431,7 +430,6 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad } /** - * @inheritdoc IPufferProtocol * @dev Initially it is restricted to the DAO */ function createPufferModule(bytes32 moduleName) external restricted returns (address) { @@ -439,7 +437,6 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad } /** - * @inheritdoc IPufferProtocol * @dev Restricted to the DAO */ function setModuleWeights(bytes32[] calldata newModuleWeights) external restricted { @@ -447,7 +444,6 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad } /** - * @inheritdoc IPufferProtocol * @dev Restricted to the DAO */ function setValidatorLimitPerModule(bytes32 moduleName, uint128 limit) external restricted { @@ -455,7 +451,6 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad } /** - * @inheritdoc IPufferProtocol * @dev Restricted to the DAO */ function setVTPenalty(uint256 newPenaltyAmount) external restricted { diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index d00c43ec..6e8e766a 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -57,11 +57,8 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { newDelegationApprover: newDelegationApprover }); - IDelegationManager.OperatorDetails memory details = - operator.EIGEN_DELEGATION_MANAGER().operatorDetails(address(operator)); - assertEq(details.delegationApprover, newDelegationApprover, "updated delegation approver"); - - assertEq(details.__deprecated_earningsReceiver, address(this), "updated earnings"); + address result = operator.EIGEN_DELEGATION_MANAGER().delegationApprover(address(operator)); + assertEq(result, newDelegationApprover, "updated delegation approver"); } function test_update_metadata_uri() public { @@ -194,15 +191,11 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { function _createRestakingOperator() internal returns (RestakingOperator) { RestakingOperator operator = moduleManager.createNewRestakingOperator({ metadataURI: "https://puffer.fi/metadata.json", - delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 + delegationApprover: address(0x5555555555555555555555555555555555555555), + allocationDelay: 0 }); - IDelegationManager.OperatorDetails memory details = - operator.EIGEN_DELEGATION_MANAGER().operatorDetails(address(operator)); - assertEq(details.delegationApprover, address(0), "delegation approver"); - assertEq(details.stakerOptOutWindowBlocks, 0, "blocks"); - assertEq(details.__deprecated_earningsReceiver, address(moduleManager), "earnings receiver"); + assertTrue(address(operator).code.length > 0, "operator deployed"); return operator; } diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol index e18df738..e3ac1f25 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol @@ -7,7 +7,6 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils import { DeployEverything } from "script/DeployEverything.s.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { PufferModule } from "../../src/PufferModule.sol"; -import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.sol"; import { AVSContractsRegistry } from "../../src/AVSContractsRegistry.sol"; import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; @@ -22,6 +21,7 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; +import { IDelegationManagerTypes } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; interface Weth { function deposit() external payable; @@ -50,7 +50,7 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { address ACCESS_MANAGER_HOLESKY = 0xA6c916f85DAfeb6f726E03a1Ce8d08cf835138fF; address MODULE_BEACON_HOLESKY = 0x5B81A4579f466fB17af4d8CC0ED51256b94c61D4; address PUFFER_PROTOCOL_HOLESKY = 0x705E27D6A6A0c77081D32C07DbDE5A1E139D3F14; - address PUFFER_MODULE_MANAGER = 0xe4695ab93163F91665Ce5b96527408336f070a71; + address payable PUFFER_MODULE_MANAGER = payable(0xe4695ab93163F91665Ce5b96527408336f070a71); address PUFFER_MODULE_0_HOLESKY = 0x0B0456ec773B7D89C9deCc38b682F98556CF9862; // https://holesky.eigenlayer.xyz/operator/0xe2c2dc296a0bff351f6bc3e98d37ea798e393e56 address RESTAKING_OPERATOR_CONTRACT = 0xe2c2dc296a0bFF351F6bC3e98D37ea798e393e56; @@ -62,7 +62,7 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { // https://holesky.etherscan.io/tx/0x2d6675d7d71606a9aafcd9f0d8a65c8bad3d7c0ed7915bb67290e989a3c8f1c6#eventlog vm.createSelectFork(vm.rpcUrl("holesky"), 1369706); - IPufferModuleManager pufferModuleManager = IPufferModuleManager(PUFFER_MODULE_MANAGER); + PufferModuleManager pufferModuleManager = PufferModuleManager(PUFFER_MODULE_MANAGER); // Upgrade PufferModule to a new implementation, that fixes the issue PufferModule upgrade = new PufferModule({ @@ -98,15 +98,15 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { uint256[] memory shares = new uint256[](1); shares[0] = 224000000000000000000; - IDelegationManager.Withdrawal[] memory withdrawals = new IDelegationManager.Withdrawal[](1); - withdrawals[0] = IDelegationManager.Withdrawal({ + IDelegationManagerTypes.Withdrawal[] memory withdrawals = new IDelegationManagerTypes.Withdrawal[](1); + withdrawals[0] = IDelegationManagerTypes.Withdrawal({ staker: PUFFER_MODULE_0_HOLESKY, delegatedTo: RESTAKING_OPERATOR_CONTRACT, withdrawer: PUFFER_MODULE_0_HOLESKY, nonce: 0, startBlock: 1369340, strategies: strategies, - shares: shares + scaledShares: shares }); IERC20[] memory t = new IERC20[](1); @@ -123,7 +123,6 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { moduleName: bytes32("PUFFER_MODULE_0"), withdrawals: withdrawals, tokens: tokens, - middlewareTimesIndexes: middlewareTimesIndexes, receiveAsTokens: receiveAsTokens }); diff --git a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol index 6ccd5a3b..d86e3752 100644 --- a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol +++ b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol @@ -51,73 +51,6 @@ contract PufferModuleManagerHoleskyTestnetFFI is Test { address RESTAKING_OPERATOR_BEACON = 0xa7DC88c059F57ADcE41070cEfEFd31F74649a261; address REWARDS_COORDINATOR = 0xAcc1fb458a1317E886dB376Fc8141540537E68fE; - // This test is for the Existing Holesky Testnet deployment - // In order for this test to work, it is necessary to have the following environment variables set: OPERATOR_BLS_SK, OPERATOR_ECDSA_SK - function test_register_operator_eigen_da_holesky() public { - vm.createSelectFork(vm.rpcUrl("holesky"), 1401731); // (Apr-20-2024 04:50:24 AM +UTC) - - (, uint256 ECDSA_SK) = makeAddrAndKey("secretEcdsa"); - // not important key, only used in tests - uint256 BLS_SK = 990752502457672953874018146088155028776815267780829407860243712322774887125; - - IBLSApkRegistry.PubkeyRegistrationParams memory params = _generateBlsPubkeyParams(BLS_SK); - - // He signs with his BLS private key his pubkey to prove the BLS key ownership - BN254.G1Point memory messageHash = IRegistryCoordinatorExtended(EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY) - .pubkeyRegistrationMessageHash(RESTAKING_OPERATOR_CONTRACT); - - params.pubkeyRegistrationSignature = BN254.scalar_mul(messageHash, BLS_SK); - - // With ECDSA key, he sign the hash confirming that the operator wants to be registered to a certain restaking service - (bytes32 digestHash, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) = - _getOperatorSignature( - ECDSA_SK, - RESTAKING_OPERATOR_CONTRACT, - EIGEN_DA_SERVICE_MANAGER, - bytes32(hex"aaaabbccbbaa"), // This random salt needs to be different for every new registration - type(uint256).max - ); - - address operatorAddress = vm.addr(ECDSA_SK); - - IPufferModuleManager pufferModuleManager = IPufferModuleManager(PUFFER_MODULE_MANAGER); - - bytes memory hashCall = abi.encodeCall( - IPufferModuleManager.updateAVSRegistrationSignatureProof, - (RestakingOperator(RESTAKING_OPERATOR_CONTRACT), digestHash, operatorAddress) - ); - - vm.startPrank(PUFFER_SHARED_DEV_WALLET); // 'DAO' role on the Holesky testnet - (bool success,) = address(pufferModuleManager).call(hashCall); - assertEq(success, true, "updateAVSRegistrationSignatureProof failed"); - - console.log("updateAVSRegistrationSignatureProof calldata:"); - console.logBytes(hashCall); - // We first need to register that hash on our staking operator contract - // This can be done on chain by doing: - // cast send $PUFFER_MODULE_MANAGER hashCall --rpc-url=$HOLESKY_RPC_URL --private-key=$PUFFER_SHARED_PK - - bytes memory calldataToRegister = abi.encodeCall( - IPufferModuleManager.callRegisterOperatorToAVS, - ( - RestakingOperator(RESTAKING_OPERATOR_CONTRACT), - EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY, - bytes(hex"01"), - "20.64.16.29:32005;32004", // Update to the correct value - params, - operatorSignature - ) - ); - - console.log("callRegisterOperatorToAVS calldata:"); - console.logBytes(calldataToRegister); - // // cast send $PUFFER_MODULE_MANAGER calldataToRegister --rpc-url=$HOLESKY_RPC_URL --private-key=$PUFFER_SHARED_PK - - // Finish the registration - (success,) = address(pufferModuleManager).call(calldataToRegister); - assertEq(success, true, "register operator to avs"); - } - // Generates bls pubkey params from a private key function _generateBlsPubkeyParams(uint256 privKey) internal diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index efb18401..eaa55017 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -231,13 +231,10 @@ contract PufferModuleManagerTest is UnitTestHelper { IDelegationManager.Withdrawal[] memory withdrawals; IERC20[][] memory tokens; - uint256[] memory middlewareTimesIndexes; bool[] memory receiveAsTokens; emit IPufferModuleManager.CompletedQueuedWithdrawals(moduleName, 0); - pufferModuleManager.callCompleteQueuedWithdrawals( - moduleName, withdrawals, tokens, middlewareTimesIndexes, receiveAsTokens - ); + pufferModuleManager.callCompleteQueuedWithdrawals(moduleName, withdrawals, tokens, receiveAsTokens); } function test_updateAVSRegistrationSignatureProof() public { @@ -364,13 +361,9 @@ contract PufferModuleManagerTest is UnitTestHelper { RestakingOperator operator = pufferModuleManager.createNewRestakingOperator({ metadataURI: "https://puffer.fi/metadata.json", delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 + allocationDelay: 0 }); - IDelegationManager.OperatorDetails memory details = - operator.EIGEN_DELEGATION_MANAGER().operatorDetails(address(operator)); - assertEq(details.delegationApprover, address(0), "delegation approver"); - assertEq(details.stakerOptOutWindowBlocks, 0, "blocks"); return operator; } From a018ae915d702b4b795724ff364825885c0f1279 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 12 Dec 2024 14:16:16 +0100 Subject: [PATCH 07/37] remove unused var --- mainnet-contracts/script/DeployPuffer.s.sol | 3 +-- .../script/DeployPufferModuleManager.s.sol | 4 +--- mainnet-contracts/src/PufferModuleManager.sol | 11 +---------- ...rModuleManagerHoleskyTestnetTest.integration.t.sol | 3 +-- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/mainnet-contracts/script/DeployPuffer.s.sol b/mainnet-contracts/script/DeployPuffer.s.sol index 7702e8b7..95030a90 100644 --- a/mainnet-contracts/script/DeployPuffer.s.sol +++ b/mainnet-contracts/script/DeployPuffer.s.sol @@ -164,8 +164,7 @@ contract DeployPuffer is BaseScript { moduleManager = new PufferModuleManager({ pufferModuleBeacon: address(pufferModuleBeacon), restakingOperatorBeacon: address(restakingOperatorBeacon), - pufferProtocol: address(proxy), - avsContractsRegistry: aVSContractsRegistry + pufferProtocol: address(proxy) }); NoImplementation(payable(address(moduleManagerProxy))).upgradeToAndCall( diff --git a/mainnet-contracts/script/DeployPufferModuleManager.s.sol b/mainnet-contracts/script/DeployPufferModuleManager.s.sol index 8b5c23b0..e2a099ca 100644 --- a/mainnet-contracts/script/DeployPufferModuleManager.s.sol +++ b/mainnet-contracts/script/DeployPufferModuleManager.s.sol @@ -7,7 +7,6 @@ import { PufferProtocol } from "../src/PufferProtocol.sol"; import { BaseScript } from "script/BaseScript.s.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; -import { AVSContractsRegistry } from "../src/AVSContractsRegistry.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; /** * forge script script/DeployPufferModuleManager.s.sol:DeployPufferModuleManager -vvvv --rpc-url=$RPC_URL --broadcast --verify @@ -20,8 +19,7 @@ contract DeployPufferModuleManager is DeployerHelper { PufferModuleManager newPufferModuleManagerImplementation = new PufferModuleManager({ pufferModuleBeacon: address(_getPufferModuleBeacon()), restakingOperatorBeacon: address(_getRestakingOperatorBeacon()), - pufferProtocol: address(_getPufferProtocol()), - avsContractsRegistry: AVSContractsRegistry(_getAVSContractsRegistry()) + pufferProtocol: address(_getPufferProtocol()) }); //@todo Double check reinitialization diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 4f9a7455..912258a9 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -18,7 +18,6 @@ import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelega import { IDelegationManagerTypes } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { ISignatureUtils } from "../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { AVSContractsRegistry } from "./AVSContractsRegistry.sol"; import { RestakingOperator } from "./RestakingOperator.sol"; import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; @@ -33,8 +32,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, address public immutable PUFFER_PROTOCOL; address payable public immutable PUFFER_VAULT; - AVSContractsRegistry public immutable AVS_CONTRACTS_REGISTRY; - modifier onlyPufferProtocol() { if (msg.sender != PUFFER_PROTOCOL) { revert Unauthorized(); @@ -42,17 +39,11 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, _; } - constructor( - address pufferModuleBeacon, - address restakingOperatorBeacon, - address pufferProtocol, - AVSContractsRegistry avsContractsRegistry - ) { + constructor(address pufferModuleBeacon, address restakingOperatorBeacon, address pufferProtocol) { PUFFER_MODULE_BEACON = pufferModuleBeacon; RESTAKING_OPERATOR_BEACON = restakingOperatorBeacon; PUFFER_PROTOCOL = pufferProtocol; PUFFER_VAULT = payable(address(IPufferProtocol(PUFFER_PROTOCOL).PUFFER_VAULT())); - AVS_CONTRACTS_REGISTRY = avsContractsRegistry; _disableInitializers(); } diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol index e3ac1f25..a2854686 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol @@ -83,8 +83,7 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { PufferModuleManager moduleManagerImplementation = new PufferModuleManager({ pufferModuleBeacon: MODULE_BEACON_HOLESKY, restakingOperatorBeacon: RESTAKING_OPERATOR_BEACON, - pufferProtocol: PUFFER_PROTOCOL_HOLESKY, - avsContractsRegistry: new AVSContractsRegistry(ACCESS_MANAGER_HOLESKY) + pufferProtocol: PUFFER_PROTOCOL_HOLESKY }); // Upgrade PufferModuleManager to a new implementation From f2bb9f5daef3d59fc951de60f035027ab6f4cada Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 13 Dec 2024 13:05:54 +0100 Subject: [PATCH 08/37] update mocks / test --- .../test/mocks/DelegationManagerMock.sol | 15 +++--- .../test/mocks/EigenPodManagerMock.sol | 10 +++- .../test/unit/L1RewardManager.t.sol | 18 ------- .../test/unit/PufferModuleManager.t.sol | 52 ++----------------- 4 files changed, 20 insertions(+), 75 deletions(-) diff --git a/mainnet-contracts/test/mocks/DelegationManagerMock.sol b/mainnet-contracts/test/mocks/DelegationManagerMock.sol index fa8958a5..d9a74df4 100644 --- a/mainnet-contracts/test/mocks/DelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/DelegationManagerMock.sol @@ -22,15 +22,12 @@ contract DelegationManagerMock { mapping(address => address) public delegatedTo; - function registerAsOperator( - IDelegationManager.OperatorDetails calldata, /*registeringOperatorDetails*/ - string calldata /*metadataURI*/ - ) external pure { } + function registerAsOperator(address initDelegationApprover, uint32 allocationDelay, string calldata metadataURI) + external + { } function updateOperatorMetadataURI(string calldata /*metadataURI*/ ) external pure { } - function updateAVSMetadataURI(string calldata /*metadataURI*/ ) external pure { } - function delegateTo( address operator, IDelegationManager.SignatureWithExpiry memory, /*approverSignatureAndExpiry*/ @@ -217,4 +214,10 @@ contract DelegationManagerMock { ) external { } function operatorDetails(address operator) external view returns (IDelegationManager.OperatorDetails memory) { } + + function completeQueuedWithdrawals( + IDelegationManager.Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens + ) external { } } diff --git a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol index 3f0be83b..ae36d8ae 100644 --- a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol @@ -5,7 +5,11 @@ import "forge-std/Test.sol"; import "src/interface/EigenLayer-Slashing/IEigenPodManager.sol"; import "src/interface/EigenLayer-Slashing/IAllocationManager.sol"; -contract EigenPodMock { } +contract EigenPodMock { + function startCheckpoint(bool) external { } + + function setProofSubmitter(address) external { } +} contract EigenPodManagerMock is IEigenPodManager, Test { /// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue @@ -37,7 +41,9 @@ contract EigenPodManagerMock is IEigenPodManager, Test { * @dev Function will revert if the `msg.sender` already has an EigenPod. * @dev Returns EigenPod address */ - function createPod() external returns (address) { } + function createPod() external returns (address) { + return address(new EigenPodMock()); + } /** * @notice Stakes for a new beacon chain validator on the sender's EigenPod. diff --git a/mainnet-contracts/test/unit/L1RewardManager.t.sol b/mainnet-contracts/test/unit/L1RewardManager.t.sol index 970b812d..6ce40d6e 100644 --- a/mainnet-contracts/test/unit/L1RewardManager.t.sol +++ b/mainnet-contracts/test/unit/L1RewardManager.t.sol @@ -17,8 +17,6 @@ import { } from "../../script/Roles.sol"; import { InvalidAddress, Unauthorized } from "mainnet-contracts/src/Errors.sol"; import { GenerateAccessManagerCalldata3 } from "script/AccessManagerMigrations/GenerateAccessManagerCalldata3.s.sol"; -import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import { Options } from "openzeppelin-foundry-upgrades/Options.sol"; contract L1RewardManagerTest is UnitTestHelper { uint256 rewardsAmount; @@ -346,20 +344,4 @@ contract L1RewardManagerTest is UnitTestHelper { vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector)); l1RewardManager.xReceive(bytes32(0), 0, address(0), address(l2RewardManager), 4123123, ""); } - - function test_upgrade_plugin() public { - Options memory opts; - - opts.constructorData = abi.encode(address(0), address(pufferVault), address(0), address(l2RewardManager)); - - address proxy = Upgrades.deployUUPSProxy( - "L1RewardManager.sol", abi.encodeCall(L1RewardManager.initialize, (address(accessManager))), opts - ); - - vm.startPrank(address(timelock)); - - // It should revert because the new implementation is not good - vm.expectRevert(); - Upgrades.upgradeProxy(proxy, "L1RewardManagerUnsafe.sol:L1RewardManagerUnsafe", "", opts); - } } diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index eaa55017..8e5c112c 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -277,56 +277,10 @@ contract PufferModuleManagerTest is UnitTestHelper { bytes memory customCalldata = abi.encodeCall(PufferModuleManagerTest.getMagicNumber, ()); - // Not allowlisted, revert - vm.expectRevert(); - pufferModuleManager.customExternalCall(operator, address(this), customCalldata); - - // Generate allowlist cd - bytes memory allowlistCalldata = abi.encodeWithSelector( - AVSContractsRegistry.setAvsRegistryCoordinator.selector, - address(this), - PufferModuleManagerTest.getMagicNumber.selector, - true - ); - - // Schedule adding the registry coordinator contract (this contract as a mock) to the allowlist - // accessManager.schedule(address(avsContractsRegistry), allowlistCalldata, 0); - - // Advance the timestamp - vm.warp(block.timestamp + 1 days + 1); - // execute the allowlist calldata - accessManager.execute(address(avsContractsRegistry), allowlistCalldata); - - // Now it works vm.expectEmit(true, true, true, true); - emit IPufferModuleManager.CustomCallSucceeded({ - restakingOperator: address(operator), - target: address(this), - customCalldata: customCalldata, - response: abi.encode(85858585) - }); - pufferModuleManager.customExternalCall( - operator, address(this), abi.encodeCall(PufferModuleManagerTest.getMagicNumber, ()) - ); - - // Generate allowlist cd to remove the selector from the allowlist - allowlistCalldata = abi.encodeWithSelector( - AVSContractsRegistry.setAvsRegistryCoordinator.selector, - address(this), - PufferModuleManagerTest.getMagicNumber.selector, - false + emit IPufferModuleManager.CustomCallSucceeded( + address(operator), address(this), customCalldata, abi.encode(85858585) ); - - // Schedule adding the registry coordinator contract (this contract as a mock) to the allowlist - // accessManager.schedule(address(avsContractsRegistry), allowlistCalldata, 0); - - // Advance the timestamp - vm.warp(block.timestamp + 1 days + 1); - // execute the allowlist calldata - accessManager.execute(address(avsContractsRegistry), allowlistCalldata); - - // Not allowlisted, revert - vm.expectRevert(); pufferModuleManager.customExternalCall(operator, address(this), customCalldata); vm.stopPrank(); } @@ -361,7 +315,7 @@ contract PufferModuleManagerTest is UnitTestHelper { RestakingOperator operator = pufferModuleManager.createNewRestakingOperator({ metadataURI: "https://puffer.fi/metadata.json", delegationApprover: address(0), - allocationDelay: 0 + allocationDelay: 500 }); return operator; From 2af9783d3349771800c754bcdb6240c160be6792 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 13 Dec 2024 13:15:08 +0100 Subject: [PATCH 09/37] remove unused code --- .../interface/libraries/BeaconChainProofs.sol | 269 ---------- .../src/interface/libraries/BytesLib.sol | 479 ------------------ .../src/interface/libraries/Endian.sol | 25 - .../src/interface/libraries/Merkle.sol | 163 ------ .../interface/libraries/OperatorSetLib.sol | 14 - .../src/interface/libraries/SlashingLib.sol | 170 ------- .../src/interface/libraries/Snapshots.sol | 182 ------- 7 files changed, 1302 deletions(-) delete mode 100644 mainnet-contracts/src/interface/libraries/BytesLib.sol delete mode 100644 mainnet-contracts/src/interface/libraries/Endian.sol delete mode 100644 mainnet-contracts/src/interface/libraries/Merkle.sol delete mode 100644 mainnet-contracts/src/interface/libraries/SlashingLib.sol delete mode 100644 mainnet-contracts/src/interface/libraries/Snapshots.sol diff --git a/mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol b/mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol index 880efcff..f77e2af0 100644 --- a/mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol +++ b/mainnet-contracts/src/interface/libraries/BeaconChainProofs.sol @@ -2,75 +2,11 @@ pragma solidity ^0.8.0; -import "./Merkle.sol"; -import "../libraries/Endian.sol"; - //Utility library for parsing and PHASE0 beacon chain block headers //SSZ Spec: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization //BeaconBlockHeader Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader //BeaconState Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconstate library BeaconChainProofs { - /// @dev Thrown when a proof is invalid. - error InvalidProof(); - /// @dev Thrown when a proof with an invalid length is provided. - error InvalidProofLength(); - /// @dev Thrown when a validator fields length is invalid. - error InvalidValidatorFieldsLength(); - - /// @notice Heights of various merkle trees in the beacon chain - /// - beaconBlockRoot - /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT - /// -- beaconStateRoot - /// | HEIGHT: BEACON_STATE_TREE_HEIGHT - /// validatorContainerRoot, balanceContainerRoot - /// | | HEIGHT: BALANCE_TREE_HEIGHT - /// | individual balances - /// | HEIGHT: VALIDATOR_TREE_HEIGHT - /// individual validators - uint256 internal constant BEACON_BLOCK_HEADER_TREE_HEIGHT = 3; - uint256 internal constant BEACON_STATE_TREE_HEIGHT = 5; - uint256 internal constant BALANCE_TREE_HEIGHT = 38; - uint256 internal constant VALIDATOR_TREE_HEIGHT = 40; - - /// @notice Index of the beaconStateRoot in the `BeaconBlockHeader` container - /// - /// BeaconBlockHeader = [..., state_root, ...] - /// 0... 3 - /// - /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader) - uint256 internal constant STATE_ROOT_INDEX = 3; - - /// @notice Indices for fields in the `BeaconState` container - /// - /// BeaconState = [..., validators, balances, ...] - /// 0... 11 12 - /// - /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate) - uint256 internal constant VALIDATOR_CONTAINER_INDEX = 11; - uint256 internal constant BALANCE_CONTAINER_INDEX = 12; - - /// @notice Number of fields in the `Validator` container - /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator) - uint256 internal constant VALIDATOR_FIELDS_LENGTH = 8; - - /// @notice Indices for fields in the `Validator` container - uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0; - uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1; - uint256 internal constant VALIDATOR_BALANCE_INDEX = 2; - uint256 internal constant VALIDATOR_SLASHED_INDEX = 3; - uint256 internal constant VALIDATOR_ACTIVATION_EPOCH_INDEX = 5; - uint256 internal constant VALIDATOR_EXIT_EPOCH_INDEX = 6; - - /// @notice Slot/Epoch timings - uint64 internal constant SECONDS_PER_SLOT = 12; - uint64 internal constant SLOTS_PER_EPOCH = 32; - uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT; - - /// @notice `FAR_FUTURE_EPOCH` is used as the default value for certain `Validator` - /// fields when a `Validator` is first created on the beacon chain - uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max; - bytes8 internal constant UINT64_MASK = 0xffffffffffffffff; - /// @notice Contains a beacon state root and a merkle proof verifying its inclusion under a beacon block root struct StateRootProof { bytes32 beaconStateRoot; @@ -95,209 +31,4 @@ library BeaconChainProofs { bytes32 balanceRoot; bytes proof; } - - /** - * - * VALIDATOR FIELDS -> BEACON STATE ROOT -> BEACON BLOCK ROOT - * - */ - - /// @notice Verify a merkle proof of the beacon state root against a beacon block root - /// @param beaconBlockRoot merkle root of the beacon block - /// @param proof the beacon state root and merkle proof of its inclusion under `beaconBlockRoot` - function verifyStateRoot(bytes32 beaconBlockRoot, StateRootProof calldata proof) internal view { - require(proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT), InvalidProofLength()); - - /// This merkle proof verifies the `beaconStateRoot` under the `beaconBlockRoot` - /// - beaconBlockRoot - /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT - /// -- beaconStateRoot - require( - Merkle.verifyInclusionSha256({ - proof: proof.proof, - root: beaconBlockRoot, - leaf: proof.beaconStateRoot, - index: STATE_ROOT_INDEX - }), - InvalidProof() - ); - } - - /// @notice Verify a merkle proof of a validator container against a `beaconStateRoot` - /// @dev This proof starts at a validator's container root, proves through the validator container root, - /// and continues proving to the root of the `BeaconState` - /// @dev See https://eth2book.info/capella/part3/containers/dependencies/#validator for info on `Validator` containers - /// @dev See https://eth2book.info/capella/part3/containers/state/#beaconstate for info on `BeaconState` containers - /// @param beaconStateRoot merkle root of the `BeaconState` container - /// @param validatorFields an individual validator's fields. These are merklized to form a `validatorRoot`, - /// which is used as the leaf to prove against `beaconStateRoot` - /// @param validatorFieldsProof a merkle proof of inclusion of `validatorFields` under `beaconStateRoot` - /// @param validatorIndex the validator's unique index - function verifyValidatorFields( - bytes32 beaconStateRoot, - bytes32[] calldata validatorFields, - bytes calldata validatorFieldsProof, - uint40 validatorIndex - ) internal view { - require(validatorFields.length == VALIDATOR_FIELDS_LENGTH, InvalidValidatorFieldsLength()); - - /// Note: the reason we use `VALIDATOR_TREE_HEIGHT + 1` here is because the merklization process for - /// this container includes hashing the root of the validator tree with the length of the validator list - require( - validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_TREE_HEIGHT), - InvalidProofLength() - ); - - // Merkleize `validatorFields` to get the leaf to prove - bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields); - - /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees: - /// - beaconStateRoot - /// | HEIGHT: BEACON_STATE_TREE_HEIGHT - /// -- validatorContainerRoot - /// | HEIGHT: VALIDATOR_TREE_HEIGHT + 1 - /// ---- validatorRoot - uint256 index = (VALIDATOR_CONTAINER_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) | uint256(validatorIndex); - - require( - Merkle.verifyInclusionSha256({ - proof: validatorFieldsProof, - root: beaconStateRoot, - leaf: validatorRoot, - index: index - }), - InvalidProof() - ); - } - - /** - * - * VALIDATOR BALANCE -> BALANCE CONTAINER ROOT -> BEACON BLOCK ROOT - * - */ - - /// @notice Verify a merkle proof of the beacon state's balances container against the beacon block root - /// @dev This proof starts at the balance container root, proves through the beacon state root, and - /// continues proving through the beacon block root. As a result, this proof will contain elements - /// of a `StateRootProof` under the same block root, with the addition of proving the balances field - /// within the beacon state. - /// @dev This is used to make checkpoint proofs more efficient, as a checkpoint will verify multiple balances - /// against the same balance container root. - /// @param beaconBlockRoot merkle root of the beacon block - /// @param proof a beacon balance container root and merkle proof of its inclusion under `beaconBlockRoot` - function verifyBalanceContainer(bytes32 beaconBlockRoot, BalanceContainerProof calldata proof) internal view { - require( - proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT + BEACON_STATE_TREE_HEIGHT), - InvalidProofLength() - ); - - /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees: - /// - beaconBlockRoot - /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT - /// -- beaconStateRoot - /// | HEIGHT: BEACON_STATE_TREE_HEIGHT - /// ---- balancesContainerRoot - uint256 index = (STATE_ROOT_INDEX << (BEACON_STATE_TREE_HEIGHT)) | BALANCE_CONTAINER_INDEX; - - require( - Merkle.verifyInclusionSha256({ - proof: proof.proof, - root: beaconBlockRoot, - leaf: proof.balanceContainerRoot, - index: index - }), - InvalidProof() - ); - } - - /// @notice Verify a merkle proof of a validator's balance against the beacon state's `balanceContainerRoot` - /// @param balanceContainerRoot the merkle root of all validators' current balances - /// @param validatorIndex the index of the validator whose balance we are proving - /// @param proof the validator's associated balance root and a merkle proof of inclusion under `balanceContainerRoot` - /// @return validatorBalanceGwei the validator's current balance (in gwei) - function verifyValidatorBalance(bytes32 balanceContainerRoot, uint40 validatorIndex, BalanceProof calldata proof) - internal - view - returns (uint64 validatorBalanceGwei) - { - /// Note: the reason we use `BALANCE_TREE_HEIGHT + 1` here is because the merklization process for - /// this container includes hashing the root of the balances tree with the length of the balances list - require(proof.proof.length == 32 * (BALANCE_TREE_HEIGHT + 1), InvalidProofLength()); - - /// When merkleized, beacon chain balances are combined into groups of 4 called a `balanceRoot`. The merkle - /// proof here verifies that this validator's `balanceRoot` is included in the `balanceContainerRoot` - /// - balanceContainerRoot - /// | HEIGHT: BALANCE_TREE_HEIGHT - /// -- balanceRoot - uint256 balanceIndex = uint256(validatorIndex / 4); - - require( - Merkle.verifyInclusionSha256({ - proof: proof.proof, - root: balanceContainerRoot, - leaf: proof.balanceRoot, - index: balanceIndex - }), - InvalidProof() - ); - - /// Extract the individual validator's balance from the `balanceRoot` - return getBalanceAtIndex(proof.balanceRoot, validatorIndex); - } - - /** - * @notice Parses a balanceRoot to get the uint64 balance of a validator. - * @dev During merkleization of the beacon state balance tree, four uint64 values are treated as a single - * leaf in the merkle tree. We use validatorIndex % 4 to determine which of the four uint64 values to - * extract from the balanceRoot. - * @param balanceRoot is the combination of 4 validator balances being proven for - * @param validatorIndex is the index of the validator being proven for - * @return The validator's balance, in Gwei - */ - function getBalanceAtIndex(bytes32 balanceRoot, uint40 validatorIndex) internal pure returns (uint64) { - uint256 bitShiftAmount = (validatorIndex % 4) * 64; - return Endian.fromLittleEndianUint64(bytes32((uint256(balanceRoot) << bitShiftAmount))); - } - - /// @notice Indices for fields in the `Validator` container: - /// 0: pubkey - /// 1: withdrawal credentials - /// 2: effective balance - /// 3: slashed? - /// 4: activation eligibility epoch - /// 5: activation epoch - /// 6: exit epoch - /// 7: withdrawable epoch - /// - /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator) - - /// @dev Retrieves a validator's pubkey hash - function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) { - return validatorFields[VALIDATOR_PUBKEY_INDEX]; - } - - /// @dev Retrieves a validator's withdrawal credentials - function getWithdrawalCredentials(bytes32[] memory validatorFields) internal pure returns (bytes32) { - return validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX]; - } - - /// @dev Retrieves a validator's effective balance (in gwei) - function getEffectiveBalanceGwei(bytes32[] memory validatorFields) internal pure returns (uint64) { - return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]); - } - - /// @dev Retrieves a validator's activation epoch - function getActivationEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) { - return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_ACTIVATION_EPOCH_INDEX]); - } - - /// @dev Retrieves true IFF a validator is marked slashed - function isValidatorSlashed(bytes32[] memory validatorFields) internal pure returns (bool) { - return validatorFields[VALIDATOR_SLASHED_INDEX] != 0; - } - - /// @dev Retrieves a validator's exit epoch - function getExitEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) { - return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_EXIT_EPOCH_INDEX]); - } } diff --git a/mainnet-contracts/src/interface/libraries/BytesLib.sol b/mainnet-contracts/src/interface/libraries/BytesLib.sol deleted file mode 100644 index 81e3d35f..00000000 --- a/mainnet-contracts/src/interface/libraries/BytesLib.sol +++ /dev/null @@ -1,479 +0,0 @@ -// SPDX-License-Identifier: Unlicense -/* - * @title Solidity Bytes Arrays Utils - * @author Gonçalo Sá - * - * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. - * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. - */ -pragma solidity >=0.8.0 <0.9.0; - -library BytesLib { - error Overflow(); - error OutOfBounds(); - - function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { - bytes memory tempBytes; - - assembly { - // Get a location of some free memory and store it in tempBytes as - // Solidity does for memory variables. - tempBytes := mload(0x40) - - // Store the length of the first bytes array at the beginning of - // the memory for tempBytes. - let length := mload(_preBytes) - mstore(tempBytes, length) - - // Maintain a memory counter for the current write location in the - // temp bytes array by adding the 32 bytes for the array length to - // the starting location. - let mc := add(tempBytes, 0x20) - // Stop copying when the memory counter reaches the length of the - // first bytes array. - let end := add(mc, length) - - for { - // Initialize a copy counter to the start of the _preBytes data, - // 32 bytes into its memory. - let cc := add(_preBytes, 0x20) - } lt(mc, end) { - // Increase both counters by 32 bytes each iteration. - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - // Write the _preBytes data into the tempBytes memory 32 bytes - // at a time. - mstore(mc, mload(cc)) - } - - // Add the length of _postBytes to the current length of tempBytes - // and store it as the new length in the first 32 bytes of the - // tempBytes memory. - length := mload(_postBytes) - mstore(tempBytes, add(length, mload(tempBytes))) - - // Move the memory counter back from a multiple of 0x20 to the - // actual end of the _preBytes data. - mc := end - // Stop copying when the memory counter reaches the new combined - // length of the arrays. - end := add(mc, length) - - for { let cc := add(_postBytes, 0x20) } lt(mc, end) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { mstore(mc, mload(cc)) } - - // Update the free-memory pointer by padding our last write location - // to 32 bytes: add 31 bytes to the end of tempBytes to move to the - // next 32 byte block, then round down to the nearest multiple of - // 32. If the sum of the length of the two arrays is zero then add - // one before rounding down to leave a blank 32 bytes (the length block with 0). - mstore( - 0x40, - and( - add(add(end, iszero(add(length, mload(_preBytes)))), 31), - not(31) // Round down to the nearest 32 bytes. - ) - ) - } - - return tempBytes; - } - - function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { - assembly { - // Read the first 32 bytes of _preBytes storage, which is the length - // of the array. (We don't need to use the offset into the slot - // because arrays use the entire slot.) - let fslot := sload(_preBytes.slot) - // Arrays of 31 bytes or less have an even value in their slot, - // while longer arrays have an odd value. The actual length is - // the slot divided by two for odd values, and the lowest order - // byte divided by two for even values. - // If the slot is even, bitwise and the slot with 255 and divide by - // two to get the length. If the slot is odd, bitwise and the slot - // with -1 and divide by two. - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) - let mlength := mload(_postBytes) - let newlength := add(slength, mlength) - // slength can contain both the length and contents of the array - // if length < 32 bytes so let's prepare for that - // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage - switch add(lt(slength, 32), lt(newlength, 32)) - case 2 { - // Since the new array still fits in the slot, we just need to - // update the contents of the slot. - // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length - sstore( - _preBytes.slot, - // all the modifications to the slot are inside this - // next block - add( - // we can just add to the slot contents because the - // bytes we want to change are the LSBs - fslot, - add( - mul( - div( - // load the bytes from memory - mload(add(_postBytes, 0x20)), - // zero all bytes to the right - exp(0x100, sub(32, mlength)) - ), - // and now shift left the number of bytes to - // leave space for the length in the slot - exp(0x100, sub(32, newlength)) - ), - // increase length by the double of the memory - // bytes length - mul(mlength, 2) - ) - ) - ) - } - case 1 { - // The stored value fits in the slot, but the combined value - // will exceed it. - // get the keccak hash to get the contents of the array - mstore(0x0, _preBytes.slot) - let sc := add(keccak256(0x0, 0x20), div(slength, 32)) - - // save new length - sstore(_preBytes.slot, add(mul(newlength, 2), 1)) - - // The contents of the _postBytes array start 32 bytes into - // the structure. Our first read should obtain the `submod` - // bytes that can fit into the unused space in the last word - // of the stored array. To get this, we read 32 bytes starting - // from `submod`, so the data we read overlaps with the array - // contents by `submod` bytes. Masking the lowest-order - // `submod` bytes allows us to add that value directly to the - // stored value. - - let submod := sub(32, slength) - let mc := add(_postBytes, submod) - let end := add(_postBytes, mlength) - let mask := sub(exp(0x100, submod), 1) - - sstore( - sc, - add( - and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00), - and(mload(mc), mask) - ) - ) - - for { - mc := add(mc, 0x20) - sc := add(sc, 1) - } lt(mc, end) { - sc := add(sc, 1) - mc := add(mc, 0x20) - } { sstore(sc, mload(mc)) } - - mask := exp(0x100, sub(mc, end)) - - sstore(sc, mul(div(mload(mc), mask), mask)) - } - default { - // get the keccak hash to get the contents of the array - mstore(0x0, _preBytes.slot) - // Start copying to the last used word of the stored array. - let sc := add(keccak256(0x0, 0x20), div(slength, 32)) - - // save new length - sstore(_preBytes.slot, add(mul(newlength, 2), 1)) - - // Copy over the first `submod` bytes of the new data as in - // case 1 above. - let slengthmod := mod(slength, 32) - // solhint-disable-next-line no-unused-vars - let mlengthmod := mod(mlength, 32) - let submod := sub(32, slengthmod) - let mc := add(_postBytes, submod) - let end := add(_postBytes, mlength) - let mask := sub(exp(0x100, submod), 1) - - sstore(sc, add(sload(sc), and(mload(mc), mask))) - - for { - sc := add(sc, 1) - mc := add(mc, 0x20) - } lt(mc, end) { - sc := add(sc, 1) - mc := add(mc, 0x20) - } { sstore(sc, mload(mc)) } - - mask := exp(0x100, sub(mc, end)) - - sstore(sc, mul(div(mload(mc), mask), mask)) - } - } - } - - function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { - require(_length + 31 >= _length, Overflow()); - require(_bytes.length >= _start + _length, OutOfBounds()); - - bytes memory tempBytes; - - assembly { - switch iszero(_length) - case 0 { - // Get a location of some free memory and store it in tempBytes as - // Solidity does for memory variables. - tempBytes := mload(0x40) - - // The first word of the slice result is potentially a partial - // word read from the original array. To read it, we calculate - // the length of that partial word and start copying that many - // bytes into the array. The first word we copy will start with - // data we don't care about, but the last `lengthmod` bytes will - // land at the beginning of the contents of the new array. When - // we're done copying, we overwrite the full first word with - // the actual length of the slice. - let lengthmod := and(_length, 31) - - // The multiplication in the next line is necessary - // because when slicing multiples of 32 bytes (lengthmod == 0) - // the following copy loop was copying the origin's length - // and then ending prematurely not copying everything it should. - let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) - let end := add(mc, _length) - - for { - // The multiplication in the next line has the same exact purpose - // as the one above. - let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) - } lt(mc, end) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { mstore(mc, mload(cc)) } - - mstore(tempBytes, _length) - - //update free-memory pointer - //allocating the array padded to 32 bytes like the compiler does now - mstore(0x40, and(add(mc, 31), not(31))) - } - //if we want a zero-length slice let's just return a zero-length array - default { - tempBytes := mload(0x40) - //zero out the 32 bytes slice we are about to return - //we need to do it because Solidity does not garbage collect - mstore(tempBytes, 0) - - mstore(0x40, add(tempBytes, 0x20)) - } - } - - return tempBytes; - } - - function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { - require(_bytes.length >= _start + 20, OutOfBounds()); - address tempAddress; - - assembly { - tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) - } - - return tempAddress; - } - - function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { - require(_bytes.length >= _start + 1, OutOfBounds()); - uint8 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x1), _start)) - } - - return tempUint; - } - - function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { - require(_bytes.length >= _start + 2, OutOfBounds()); - uint16 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x2), _start)) - } - - return tempUint; - } - - function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { - require(_bytes.length >= _start + 4, OutOfBounds()); - uint32 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x4), _start)) - } - - return tempUint; - } - - function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { - require(_bytes.length >= _start + 8, OutOfBounds()); - uint64 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x8), _start)) - } - - return tempUint; - } - - function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { - require(_bytes.length >= _start + 12, OutOfBounds()); - uint96 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0xc), _start)) - } - - return tempUint; - } - - function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { - require(_bytes.length >= _start + 16, OutOfBounds()); - uint128 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x10), _start)) - } - - return tempUint; - } - - function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { - require(_bytes.length >= _start + 32, OutOfBounds()); - uint256 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x20), _start)) - } - - return tempUint; - } - - function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { - require(_bytes.length >= _start + 32, OutOfBounds()); - bytes32 tempBytes32; - - assembly { - tempBytes32 := mload(add(add(_bytes, 0x20), _start)) - } - - return tempBytes32; - } - - function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { - bool success = true; - - assembly { - let length := mload(_preBytes) - - // if lengths don't match the arrays are not equal - switch eq(length, mload(_postBytes)) - case 1 { - // cb is a circuit breaker in the for loop since there's - // no said feature for inline assembly loops - // cb = 1 - don't breaker - // cb = 0 - break - let cb := 1 - - let mc := add(_preBytes, 0x20) - let end := add(mc, length) - - for { let cc := add(_postBytes, 0x20) } // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: - eq(add(lt(mc, end), cb), 2) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - // if any of these checks fails then arrays are not equal - if iszero(eq(mload(mc), mload(cc))) { - // unsuccess: - success := 0 - cb := 0 - } - } - } - default { - // unsuccess: - success := 0 - } - } - - return success; - } - - function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { - bool success = true; - - assembly { - // we know _preBytes_offset is 0 - let fslot := sload(_preBytes.slot) - // Decode the length of the stored array like in concatStorage(). - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) - let mlength := mload(_postBytes) - - // if lengths don't match the arrays are not equal - switch eq(slength, mlength) - case 1 { - // slength can contain both the length and contents of the array - // if length < 32 bytes so let's prepare for that - // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage - if iszero(iszero(slength)) { - switch lt(slength, 32) - case 1 { - // blank the last byte which is the length - fslot := mul(div(fslot, 0x100), 0x100) - - if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { - // unsuccess: - success := 0 - } - } - default { - // cb is a circuit breaker in the for loop since there's - // no said feature for inline assembly loops - // cb = 1 - don't breaker - // cb = 0 - break - let cb := 1 - - // get the keccak hash to get the contents of the array - mstore(0x0, _preBytes.slot) - let sc := keccak256(0x0, 0x20) - - let mc := add(_postBytes, 0x20) - let end := add(mc, mlength) - - // the next line is the loop condition: - // while(uint256(mc < end) + cb == 2) - // solhint-disable-next-line no-empty-blocks - for { } eq(add(lt(mc, end), cb), 2) { - sc := add(sc, 1) - mc := add(mc, 0x20) - } { - if iszero(eq(sload(sc), mload(mc))) { - // unsuccess: - success := 0 - cb := 0 - } - } - } - } - } - default { - // unsuccess: - success := 0 - } - } - - return success; - } -} diff --git a/mainnet-contracts/src/interface/libraries/Endian.sol b/mainnet-contracts/src/interface/libraries/Endian.sol deleted file mode 100644 index f9799bb9..00000000 --- a/mainnet-contracts/src/interface/libraries/Endian.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -library Endian { - /** - * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64 - * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type - * @return n The big endian-formatted uint64 - * @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) - * through a right-shift/shr operation. - */ - function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) { - // the number needs to be stored in little-endian encoding (ie in bytes 0-8) - n = uint64(uint256(lenum >> 192)); - // forgefmt: disable-next-item - return (n >> 56) | - ((0x00FF000000000000 & n) >> 40) | - ((0x0000FF0000000000 & n) >> 24) | - ((0x000000FF00000000 & n) >> 8) | - ((0x00000000FF000000 & n) << 8) | - ((0x0000000000FF0000 & n) << 24) | - ((0x000000000000FF00 & n) << 40) | - ((0x00000000000000FF & n) << 56); - } -} diff --git a/mainnet-contracts/src/interface/libraries/Merkle.sol b/mainnet-contracts/src/interface/libraries/Merkle.sol deleted file mode 100644 index 706b7b49..00000000 --- a/mainnet-contracts/src/interface/libraries/Merkle.sol +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: MIT -// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol) - -pragma solidity ^0.8.0; - -/** - * @dev These functions deal with verification of Merkle Tree proofs. - * - * The tree and the proofs can be generated using our - * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. - * You will find a quickstart guide in the readme. - * - * WARNING: You should avoid using leaf values that are 64 bytes long prior to - * hashing, or use a hash function other than keccak256 for hashing leaves. - * This is because the concatenation of a sorted pair of internal nodes in - * the merkle tree could be reinterpreted as a leaf value. - * OpenZeppelin's JavaScript library generates merkle trees that are safe - * against this attack out of the box. - */ -library Merkle { - error InvalidProofLength(); - - /** - * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up - * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt - * hash matches the root of the tree. The tree is built assuming `leaf` is - * the 0 indexed `index`'th leaf from the bottom left of the tree. - * - * Note this is for a Merkle tree using the keccak/sha3 hash function - */ - function verifyInclusionKeccak(bytes memory proof, bytes32 root, bytes32 leaf, uint256 index) - internal - pure - returns (bool) - { - return processInclusionProofKeccak(proof, leaf, index) == root; - } - - /** - * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up - * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt - * hash matches the root of the tree. The tree is built assuming `leaf` is - * the 0 indexed `index`'th leaf from the bottom left of the tree. - * @dev If the proof length is 0 then the leaf hash is returned. - * - * _Available since v4.4._ - * - * Note this is for a Merkle tree using the keccak/sha3 hash function - */ - function processInclusionProofKeccak(bytes memory proof, bytes32 leaf, uint256 index) - internal - pure - returns (bytes32) - { - require(proof.length % 32 == 0, InvalidProofLength()); - bytes32 computedHash = leaf; - for (uint256 i = 32; i <= proof.length; i += 32) { - if (index % 2 == 0) { - // if ith bit of index is 0, then computedHash is a left sibling - assembly { - mstore(0x00, computedHash) - mstore(0x20, mload(add(proof, i))) - computedHash := keccak256(0x00, 0x40) - index := div(index, 2) - } - } else { - // if ith bit of index is 1, then computedHash is a right sibling - assembly { - mstore(0x00, mload(add(proof, i))) - mstore(0x20, computedHash) - computedHash := keccak256(0x00, 0x40) - index := div(index, 2) - } - } - } - return computedHash; - } - - /** - * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up - * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt - * hash matches the root of the tree. The tree is built assuming `leaf` is - * the 0 indexed `index`'th leaf from the bottom left of the tree. - * - * Note this is for a Merkle tree using the sha256 hash function - */ - function verifyInclusionSha256(bytes memory proof, bytes32 root, bytes32 leaf, uint256 index) - internal - view - returns (bool) - { - return processInclusionProofSha256(proof, leaf, index) == root; - } - - /** - * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up - * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt - * hash matches the root of the tree. The tree is built assuming `leaf` is - * the 0 indexed `index`'th leaf from the bottom left of the tree. - * - * _Available since v4.4._ - * - * Note this is for a Merkle tree using the sha256 hash function - */ - function processInclusionProofSha256(bytes memory proof, bytes32 leaf, uint256 index) - internal - view - returns (bytes32) - { - require(proof.length != 0 && proof.length % 32 == 0, InvalidProofLength()); - bytes32[1] memory computedHash = [leaf]; - for (uint256 i = 32; i <= proof.length; i += 32) { - if (index % 2 == 0) { - // if ith bit of index is 0, then computedHash is a left sibling - assembly { - mstore(0x00, mload(computedHash)) - mstore(0x20, mload(add(proof, i))) - if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) } - index := div(index, 2) - } - } else { - // if ith bit of index is 1, then computedHash is a right sibling - assembly { - mstore(0x00, mload(add(proof, i))) - mstore(0x20, mload(computedHash)) - if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) } - index := div(index, 2) - } - } - } - return computedHash[0]; - } - - /** - * @notice this function returns the merkle root of a tree created from a set of leaves using sha256 as its hash function - * @param leaves the leaves of the merkle tree - * @return The computed Merkle root of the tree. - * @dev A pre-condition to this function is that leaves.length is a power of two. If not, the function will merkleize the inputs incorrectly. - */ - function merkleizeSha256(bytes32[] memory leaves) internal pure returns (bytes32) { - //there are half as many nodes in the layer above the leaves - uint256 numNodesInLayer = leaves.length / 2; - //create a layer to store the internal nodes - bytes32[] memory layer = new bytes32[](numNodesInLayer); - //fill the layer with the pairwise hashes of the leaves - for (uint256 i = 0; i < numNodesInLayer; i++) { - layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])); - } - //the next layer above has half as many nodes - numNodesInLayer /= 2; - //while we haven't computed the root - while (numNodesInLayer != 0) { - //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children - for (uint256 i = 0; i < numNodesInLayer; i++) { - layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1])); - } - //the next layer above has half as many nodes - numNodesInLayer /= 2; - } - //the first node in the layer is the root - return layer[0]; - } -} diff --git a/mainnet-contracts/src/interface/libraries/OperatorSetLib.sol b/mainnet-contracts/src/interface/libraries/OperatorSetLib.sol index a087a7fd..c14b611d 100644 --- a/mainnet-contracts/src/interface/libraries/OperatorSetLib.sol +++ b/mainnet-contracts/src/interface/libraries/OperatorSetLib.sol @@ -5,17 +5,3 @@ struct OperatorSet { address avs; uint32 id; } - -library OperatorSetLib { - function key(OperatorSet memory os) internal pure returns (bytes32) { - return bytes32(abi.encodePacked(os.avs, uint96(os.id))); - } - - function decode(bytes32 _key) internal pure returns (OperatorSet memory) { - /// forgefmt: disable-next-item - return OperatorSet({ - avs: address(uint160(uint256(_key) >> 96)), - id: uint32(uint256(_key) & type(uint96).max) - }); - } -} diff --git a/mainnet-contracts/src/interface/libraries/SlashingLib.sol b/mainnet-contracts/src/interface/libraries/SlashingLib.sol deleted file mode 100644 index cdb6ce06..00000000 --- a/mainnet-contracts/src/interface/libraries/SlashingLib.sol +++ /dev/null @@ -1,170 +0,0 @@ -// // SPDX-License-Identifier: BUSL-1.1 -// pragma solidity ^0.8.27; - -// import "@openzeppelin/contracts/utils/math/Math.sol"; -// import "@openzeppelin-upgrades/contracts/utils/math/SafeCastUpgradeable.sol"; - -// /// @dev the stakerScalingFactor and operatorMagnitude have initial default values to 1e18 as "1" -// /// to preserve precision with uint256 math. We use `WAD` where these variables are used -// /// and divide to represent as 1 -// uint64 constant WAD = 1e18; - -// /* -// * There are 2 types of shares: -// * 1. depositShares -// * - These can be converted to an amount of tokens given a strategy -// * - by calling `sharesToUnderlying` on the strategy address (they're already tokens -// * in the case of EigenPods) -// * - These live in the storage of EPM and SM strategies -// * 2. shares -// * - For a staker, this is the amount of shares that they can withdraw -// * - For an operator, this is the sum of its staker's withdrawable shares -// * -// * Note that `withdrawal.scaledShares` is scaled for the beaconChainETHStrategy to divide by the beaconChainScalingFactor upon queueing -// * and multiply by the beaconChainScalingFactor upon withdrawal -// */ -// struct DepositScalingFactor { -// uint256 _scalingFactor; -// } - -// using SlashingLib for DepositScalingFactor global; - -// // TODO: validate order of operations everywhere -// library SlashingLib { -// using Math for uint256; -// using SlashingLib for uint256; -// using SafeCastUpgradeable for uint256; - -// // WAD MATH - -// function mulWad(uint256 x, uint256 y) internal pure returns (uint256) { -// return x.mulDiv(y, WAD); -// } - -// function divWad(uint256 x, uint256 y) internal pure returns (uint256) { -// return x.mulDiv(WAD, y); -// } - -// /** -// * @notice Used explicitly for calculating slashed magnitude, we want to ensure even in the -// * situation where an operator is slashed several times and precision has been lost over time, -// * an incoming slashing request isn't rounded down to 0 and an operator is able to avoid slashing penalties. -// */ -// function mulWadRoundUp(uint256 x, uint256 y) internal pure returns (uint256) { -// return x.mulDiv(y, WAD, Math.Rounding.Up); -// } - -// /** -// * @notice Used as part of calculating wadSlashed in the EPM to ensure that we don't overslash -// */ -// function divWadRoundUp(uint256 x, uint256 y) internal pure returns (uint256) { -// return x.mulDiv(WAD, y, Math.Rounding.Up); -// } - -// // GETTERS - -// function scalingFactor( -// DepositScalingFactor memory dsf -// ) internal pure returns (uint256) { -// return dsf._scalingFactor == 0 ? WAD : dsf._scalingFactor; -// } - -// function scaleForQueueWithdrawal( -// uint256 sharesToWithdraw, -// uint256 slashingFactor -// ) internal pure returns (uint256) { -// if (slashingFactor == 0) { -// return 0; -// } - -// return sharesToWithdraw.divWad(slashingFactor); -// } - -// function scaleForCompleteWithdrawal(uint256 scaledShares, uint256 slashingFactor) internal pure returns (uint256) { -// return scaledShares.mulWad(slashingFactor); -// } - -// /** -// * @notice Scales shares according to the difference in an operator's magnitude before and -// * after being slashed. This is used to calculate the number of slashable shares in the -// * withdrawal queue. -// * NOTE: max magnitude is guaranteed to only ever decrease. -// */ -// function scaleForBurning( -// uint256 scaledShares, -// uint64 prevMaxMagnitude, -// uint64 newMaxMagnitude -// ) internal pure returns (uint256) { -// return scaledShares.mulWad(prevMaxMagnitude - newMaxMagnitude); -// } - -// function update( -// DepositScalingFactor storage dsf, -// uint256 prevDepositShares, -// uint256 addedShares, -// uint256 slashingFactor -// ) internal { -// // If this is the staker's first deposit, set the scaling factor to -// // the inverse of slashingFactor -// if (prevDepositShares == 0) { -// dsf._scalingFactor = uint256(WAD).divWad(slashingFactor); -// return; -// } - -// /** -// * Base Equations: -// * (1) newShares = currentShares + addedShares -// * (2) newDepositShares = prevDepositShares + addedShares -// * (3) newShares = newDepositShares * newDepositScalingFactor * slashingFactor -// * -// * Plugging (1) into (3): -// * (4) newDepositShares * newDepositScalingFactor * slashingFactor = currentShares + addedShares -// * -// * Solving for newDepositScalingFactor -// * (5) newDepositScalingFactor = (currentShares + addedShares) / (newDepositShares * slashingFactor) -// * -// * Plugging in (2) into (5): -// * (7) newDepositScalingFactor = (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor) -// * Note that magnitudes must be divided by WAD for precision. Thus, -// * -// * (8) newDepositScalingFactor = WAD * (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor / WAD) -// * (9) newDepositScalingFactor = (currentShares + addedShares) * WAD / (prevDepositShares + addedShares) * WAD / slashingFactor -// */ - -// // Step 1: Calculate Numerator -// uint256 currentShares = dsf.calcWithdrawable(prevDepositShares, slashingFactor); - -// // Step 2: Compute currentShares + addedShares -// uint256 newShares = currentShares + addedShares; - -// // Step 3: Calculate newDepositScalingFactor -// /// forgefmt: disable-next-item -// uint256 newDepositScalingFactor = newShares -// .divWad(prevDepositShares + addedShares) -// .divWad(slashingFactor); - -// dsf._scalingFactor = newDepositScalingFactor; -// } - -// // CONVERSION - -// function calcWithdrawable( -// DepositScalingFactor memory dsf, -// uint256 depositShares, -// uint256 slashingFactor -// ) internal pure returns (uint256) { -// /// forgefmt: disable-next-item -// return depositShares -// .mulWad(dsf.scalingFactor()) -// .mulWad(slashingFactor); -// } - -// function calcSlashedAmount( -// uint256 operatorShares, -// uint256 prevMaxMagnitude, -// uint256 newMaxMagnitude -// ) internal pure returns (uint256) { -// // round up mulDiv so we don't overslash -// return operatorShares - operatorShares.mulDiv(newMaxMagnitude, prevMaxMagnitude, Math.Rounding.Up); -// } -// } diff --git a/mainnet-contracts/src/interface/libraries/Snapshots.sol b/mainnet-contracts/src/interface/libraries/Snapshots.sol deleted file mode 100644 index 6eb27727..00000000 --- a/mainnet-contracts/src/interface/libraries/Snapshots.sol +++ /dev/null @@ -1,182 +0,0 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.0; - -// import "@openzeppelin/contracts/utils/math/Math.sol"; - -// /** -// * @title Library for handling snapshots as part of allocating and slashing. -// * @notice This library is using OpenZeppelin's CheckpointsUpgradeable library (v4.9.0) -// * and removes structs and functions that are unessential. -// * Interfaces and structs are renamed for clarity and usage. -// * Some additional functions have also been added for convenience. -// * @dev This library defines the `DefaultWadHistory` and `DefaultZeroHistory` struct, for snapshotting values as they change at different points in -// * time, and later looking up past values by block number. See {Votes} as an example. -// * -// * To create a history of snapshots define a variable type `Snapshots.DefaultWadHistory` or `Snapshots.DefaultZeroHistory` in your contract, -// * and store a new snapshot for the current transaction block using the {push} function. If there is no history yet, the value is either WAD or 0, -// * depending on the type of History struct used. This is implemented because for the AllocationManager we want the -// * the default value to be WAD(1e18) but when used in the DelegationManager we want the default value to be 0. -// * -// * _Available since v4.5._ -// */ -// library Snapshots { -// struct DefaultWadHistory { -// Snapshot[] _snapshots; -// } - -// struct DefaultZeroHistory { -// Snapshot[] _snapshots; -// } - -// struct Snapshot { -// uint32 _key; -// uint224 _value; -// } - -// error InvalidSnapshotOrdering(); - -// /** -// * @dev Pushes a (`key`, `value`) pair into a DefaultWadHistory so that it is stored as the snapshot. -// */ -// function push(DefaultWadHistory storage self, uint32 key, uint64 value) internal { -// _insert(self._snapshots, key, value); -// } - -// /** -// * @dev Pushes a (`key`, `value`) pair into a DefaultZeroHistory so that it is stored as the snapshot. -// * `value` is cast to uint224. Responsibility for the safety of this operation falls outside of this library. -// */ -// function push(DefaultZeroHistory storage self, uint32 key, uint256 value) internal { -// _insert(self._snapshots, key, uint224(value)); -// } - -// /** -// * @dev Return default value of WAD if there are no snapshots for DefaultWadHistory. -// * This is used for looking up maxMagnitudes in the AllocationManager. -// */ -// function upperLookup(DefaultWadHistory storage self, uint32 key) internal view returns (uint64) { -// return uint64(_upperLookup(self._snapshots, key, WAD)); -// } - -// /** -// * @dev Return default value of 0 if there are no snapshots for DefaultZeroHistory. -// * This is used for looking up cumulative scaled shares in the DelegationManager. -// */ -// function upperLookup(DefaultZeroHistory storage self, uint32 key) internal view returns (uint256) { -// return _upperLookup(self._snapshots, key, 0); -// } - -// /** -// * @dev Returns the value in the most recent snapshot, or WAD if there are no snapshots. -// */ -// function latest( -// DefaultWadHistory storage self -// ) internal view returns (uint64) { -// return uint64(_latest(self._snapshots, WAD)); -// } - -// /** -// * @dev Returns the value in the most recent snapshot, or 0 if there are no snapshots. -// */ -// function latest( -// DefaultZeroHistory storage self -// ) internal view returns (uint256) { -// return uint256(_latest(self._snapshots, 0)); -// } - -// /** -// * @dev Returns the number of snapshots. -// */ -// function length( -// DefaultWadHistory storage self -// ) internal view returns (uint256) { -// return self._snapshots.length; -// } - -// /** -// * @dev Returns the number of snapshots. -// */ -// function length( -// DefaultZeroHistory storage self -// ) internal view returns (uint256) { -// return self._snapshots.length; -// } - -// /** -// * @dev Pushes a (`key`, `value`) pair into an ordered list of snapshots, either by inserting a new snapshot, -// * or by updating the last one. -// */ -// function _insert(Snapshot[] storage self, uint32 key, uint224 value) private { -// uint256 pos = self.length; - -// if (pos > 0) { -// // Validate that inserted keys are always >= the previous key -// Snapshot memory last = _unsafeAccess(self, pos - 1); -// require(last._key <= key, InvalidSnapshotOrdering()); - -// // Update existing snapshot if `key` matches -// if (last._key == key) { -// _unsafeAccess(self, pos - 1)._value = value; -// return; -// } -// } - -// // `key` was not in the list; push as a new entry -// self.push(Snapshot({_key: key, _value: value})); -// } - -// /** -// * @dev Returns the value in the last (most recent) snapshot with key lower or equal than the search key, or `defaultValue` if there is none. -// */ -// function _upperLookup( -// Snapshot[] storage snapshots, -// uint32 key, -// uint224 defaultValue -// ) private view returns (uint224) { -// uint256 len = snapshots.length; -// uint256 pos = _upperBinaryLookup(snapshots, key, 0, len); -// return pos == 0 ? defaultValue : _unsafeAccess(snapshots, pos - 1)._value; -// } - -// /** -// * @dev Returns the value in the most recent snapshot, or `defaultValue` if there are no snapshots. -// */ -// function _latest(Snapshot[] storage snapshots, uint224 defaultValue) private view returns (uint224) { -// uint256 pos = snapshots.length; -// return pos == 0 ? defaultValue : _unsafeAccess(snapshots, pos - 1)._value; -// } - -// /** -// * @dev Return the index of the last (most recent) snapshot with key lower or equal than the search key, or `high` if there is none. -// * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. -// * -// * WARNING: `high` should not be greater than the array's length. -// */ -// function _upperBinaryLookup( -// Snapshot[] storage self, -// uint32 key, -// uint256 low, -// uint256 high -// ) private view returns (uint256) { -// while (low < high) { -// uint256 mid = MathUpgradeable.average(low, high); -// if (_unsafeAccess(self, mid)._key > key) { -// high = mid; -// } else { -// low = mid + 1; -// } -// } -// return high; -// } - -// /** -// * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. -// */ -// function _unsafeAccess(Snapshot[] storage self, uint256 pos) private pure returns (Snapshot storage result) { -// assembly { -// mstore(0, self.slot) -// result.slot := add(keccak256(0, 0x20), pos) -// } -// } -// } From 57552d52cd76476ce8b316aea9d33a4cdeafe7d9 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 13 Dec 2024 13:25:43 +0100 Subject: [PATCH 10/37] codespell --- .../Eigenlayer-Slashing/IAVSDirectory.sol | 2 +- .../Eigenlayer-Slashing/IDelegationManager.sol | 2 +- .../interface/Eigenlayer-Slashing/IEigenPod.sol | 2 +- .../Eigenlayer-Slashing/IPermissionController.sol | 2 +- .../Eigenlayer-Slashing/IRewardsCoordinator.sol | 14 +++++++------- .../test/mocks/EigenLayerDelegationManagerMock.sol | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol index 4fd2ca05..b145d60c 100644 --- a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol @@ -41,7 +41,7 @@ interface IAVSDirectoryTypes { interface IAVSDirectoryEvents is IAVSDirectoryTypes { /** - * @notice Emitted when an operator's registration status with an AVS id udpated + * @notice Emitted when an operator's registration status with an AVS id updated * @notice Only used by legacy M2 AVSs that have not integrated with operatorSets. */ event OperatorAVSRegistrationStatusUpdated( diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol index ff983815..00f0293a 100644 --- a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IDelegationManager.sol @@ -290,7 +290,7 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele ) external; /** - * @notice Used to complete the lastest queued withdrawal. + * @notice Used to complete the latest queued withdrawal. * @param withdrawal The withdrawal to complete. * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array. * @param receiveAsTokens If true, the shares calculated to be withdrawn will be withdrawn from the specified strategies themselves diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol index ec7f2e7e..b465f711 100644 --- a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IEigenPod.sol @@ -72,7 +72,7 @@ interface IEigenPodErrors { interface IEigenPodTypes { enum VALIDATOR_STATUS { - INACTIVE, // doesnt exist + INACTIVE, // doesn't exist ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod WITHDRAWN // withdrawn from the Beacon Chain diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol index 00337742..02320ef3 100644 --- a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IPermissionController.sol @@ -118,7 +118,7 @@ interface IPermissionController is IPermissionControllerErrors, IPermissionContr function getPendingAdmins(address account) external view returns (address[] memory); /** - * @notice Checks if the given caller has permissions to call the fucntion + * @notice Checks if the given caller has permissions to call the function * @param account to check * @param caller to check permission for * @param target to check permission for diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol index c858dceb..fb8282bb 100644 --- a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol @@ -38,13 +38,13 @@ interface IRewardsCoordinatorErrors { error SplitExceedsMax(); /// @dev Thrown when input `duration` exceeds maximum. error DurationExceedsMax(); - /// @dev Thrown when input `duration` is not evenly divisble by CALCULATION_INTERVAL_SECONDS. + /// @dev Thrown when input `duration` is not evenly divisible by CALCULATION_INTERVAL_SECONDS. error InvalidDurationRemainder(); - /// @dev Thrown when GENESIS_REWARDS_TIMESTAMP is not evenly divisble by CALCULATION_INTERVAL_SECONDS. + /// @dev Thrown when GENESIS_REWARDS_TIMESTAMP is not evenly divisible by CALCULATION_INTERVAL_SECONDS. error InvalidGenesisRewardsTimestampRemainder(); - /// @dev Thrown when CALCULATION_INTERVAL_SECONDS is not evenly divisble by SNAPSHOT_CADENCE. + /// @dev Thrown when CALCULATION_INTERVAL_SECONDS is not evenly divisible by SNAPSHOT_CADENCE. error InvalidCalculationIntervalSecondsRemainder(); - /// @dev Thrown when `startTimestamp` is not evenly divisble by CALCULATION_INTERVAL_SECONDS. + /// @dev Thrown when `startTimestamp` is not evenly divisible by CALCULATION_INTERVAL_SECONDS. error InvalidStartTimestampRemainder(); /// @dev Thrown when `startTimestamp` is too far in the future. error StartTimestampTooFarInFuture(); @@ -67,7 +67,7 @@ interface IRewardsCoordinatorErrors { error InvalidTokenLeafIndex(); /// @dev Thrown when an invalid earner leaf index is provided. error InvalidEarnerLeafIndex(); - /// @dev Thrown when cummulative earnings are not greater than cummulative claimed. + /// @dev Thrown when cumulative earnings are not greater than cumulative claimed. error EarningsNotGreaterThanClaimed(); /// Reward Root Checks @@ -275,7 +275,7 @@ interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes { OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission ); - /// @notice rewardsUpdater is responsible for submiting DistributionRoots, only owner can set rewardsUpdater + /// @notice rewardsUpdater is responsible for submitting DistributionRoots, only owner can set rewardsUpdater event RewardsUpdaterSet(address indexed oldRewardsUpdater, address indexed newRewardsUpdater); event RewardsForAllSubmitterSet( @@ -539,7 +539,7 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE /// @notice Mapping: claimer => token => total amount claimed function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256); - /// @notice the defautl split for all operators across all avss + /// @notice the default split for all operators across all avss function defaultOperatorSplitBips() external view returns (uint16); /// @notice the split for a specific `operator` for a specific `avs` diff --git a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol index eda03cc9..30440d9d 100644 --- a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol @@ -114,7 +114,7 @@ contract EigenLayerDelegationManagerMock is IDelegationManager { ) external { } /** - * @notice Used to complete the lastest queued withdrawal. + * @notice Used to complete the latest queued withdrawal. * @param withdrawal The withdrawal to complete. * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array. * @param receiveAsTokens If true, the shares calculated to be withdrawn will be withdrawn from the specified strategies themselves From 55956e0fa234ace314e961f95cfc53065ea36d45 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 13 Dec 2024 13:39:05 +0100 Subject: [PATCH 11/37] more weight loss --- mainnet-contracts/package.json | 1 - mainnet-contracts/remappings.txt | 4 - .../IRegistryCoordinatorExtended.sol | 57 --- .../src/interface/libraries/BN254.sol | 334 ++++++++++++++++++ .../PufferModuleManager.integration.t.sol | 160 ++------- ...anagerHoleskyTestnetTest.integration.t.sol | 166 ++++----- .../ffi/PufferModuleManagerHoleskyFfi.t.sol | 21 +- 7 files changed, 431 insertions(+), 312 deletions(-) delete mode 100644 mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol create mode 100644 mainnet-contracts/src/interface/libraries/BN254.sol diff --git a/mainnet-contracts/package.json b/mainnet-contracts/package.json index 2c1c28fb..e5c4ca12 100644 --- a/mainnet-contracts/package.json +++ b/mainnet-contracts/package.json @@ -10,7 +10,6 @@ "@connext/interfaces": "^2.0.5", "@openzeppelin/contracts": "5.0.1", "@openzeppelin/contracts-upgradeable": "5.0.1", - "eigenlayer-middleware": "https://github.com/bxmmm1/eigenlayer-middleware.git#dbf6c1a", "l2-contracts": "*", "murky": "https://github.com/dmfxyz/murky.git", "openzeppelin-foundry-upgrades": "https://github.com/bxmmm1/openzeppelin-foundry-upgrades.git#patch-1", diff --git a/mainnet-contracts/remappings.txt b/mainnet-contracts/remappings.txt index 871af4e6..d43006e6 100644 --- a/mainnet-contracts/remappings.txt +++ b/mainnet-contracts/remappings.txt @@ -6,10 +6,6 @@ forge-std/=node_modules/forge-std/src/ @openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ @connext/=node_modules/@connext/ @crytic=node_modules/@crytic/properties -eigenlayer/=node_modules/eigenlayer-contracts/src/contracts -eigenlayer-middleware/=node_modules/eigenlayer-middleware/src/ -eigenlayer-test/=node_modules/eigenlayer-contracts/src/test -eigenlayer-contracts/=node_modules/eigenlayer-contracts/ rave/=node_modules/rave/src/ rave-test/=node_modules/rave/test/ murky/=node_modules/murky/src/ diff --git a/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol b/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol deleted file mode 100644 index bebe67e8..00000000 --- a/mainnet-contracts/src/interface/IRegistryCoordinatorExtended.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { IRegistryCoordinator, IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; -import { ISignatureUtils } from "../interface/EigenLayer-Slashing/ISignatureUtils.sol"; - -interface IRegistryCoordinatorExtended is IRegistryCoordinator { - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum - * operator capacity after the operator is registered, this method will fail. - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param socket is the socket of the operator (typically an IP address) - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ - function registerOperator( - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature - ) external; - - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator - * capacity, `operatorKickParams` is used to replace an old operator with the new one. - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorKickParams used to determine which operator is removed to maintain quorum capacity as the - * operator registers for quorums - * @param churnApproverSignature is the signature of the churnApprover over the `operatorKickParams` - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ - function registerOperatorWithChurn( - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - IRegistryCoordinator.OperatorKickParam[] calldata operatorKickParams, - ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature - ) external; - - /** - * @notice Deregisters the caller from one or more quorums - * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from - */ - function deregisterOperator(bytes calldata quorumNumbers) external; - - /** - * @notice Updates the socket of the msg.sender given they are a registered operator - * @param socket is the new socket of the operator - */ - function updateSocket(string memory socket) external; -} diff --git a/mainnet-contracts/src/interface/libraries/BN254.sol b/mainnet-contracts/src/interface/libraries/BN254.sol new file mode 100644 index 00000000..ce7fcbbe --- /dev/null +++ b/mainnet-contracts/src/interface/libraries/BN254.sol @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: MIT +// several functions are taken or adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol (MIT license): +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license + +pragma solidity >=0.8.0 <0.9.0; + +/** + * @title Library for operations on the BN254 elliptic curve. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice Contains BN254 parameters, common operations (addition, scalar mul, pairing), and BLS signature functionality. + */ +library BN254 { + // modulus for the underlying field F_p of the elliptic curve + uint256 internal constant FP_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + // modulus for the underlying field F_r of the elliptic curve + uint256 internal constant FR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + struct G1Point { + uint256 X; + uint256 Y; + } + + // Encoding of field elements is: X[1] * i + X[0] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + function generatorG1() internal pure returns (G1Point memory) { + return G1Point(1, 2); + } + + // generator of group G2 + /// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1). + uint256 internal constant G2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 internal constant G2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 internal constant G2y1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 internal constant G2y0 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + + /// @notice returns the G2 generator + /// @dev mind the ordering of the 1s and 0s! + /// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract + /// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)." + /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding + function generatorG2() internal pure returns (G2Point memory) { + return G2Point([G2x1, G2x0], [G2y1, G2y0]); + } + + // negation of the generator of group G2 + /// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1). + uint256 internal constant nG2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 internal constant nG2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052; + uint256 internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653; + + function negGeneratorG2() internal pure returns (G2Point memory) { + return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]); + } + + bytes32 internal constant powersOfTauMerkleRoot = 0x22c998e49752bbb1918ba87d6d59dd0e83620a311ba91dd4b2cc84990b31b56f; + + /** + * @param p Some point in G1. + * @return The negation of `p`, i.e. p.plus(p.negate()) should be zero. + */ + function negate(G1Point memory p) internal pure returns (G1Point memory) { + // The prime q in the base field F_q for G1 + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } else { + return G1Point(p.X, FP_MODULUS - (p.Y % FP_MODULUS)); + } + } + + /** + * @return r the sum of two points of G1 + */ + function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0x80, r, 0x40) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + + require(success, "ec-add-failed"); + } + + /** + * @notice an optimized ecMul implementation that takes O(log_2(s)) ecAdds + * @param p the point to multiply + * @param s the scalar to multiply by + * @dev this function is only safe to use if the scalar is 9 bits or less + */ + function scalar_mul_tiny(BN254.G1Point memory p, uint16 s) internal view returns (BN254.G1Point memory) { + require(s < 2 ** 9, "scalar-too-large"); + + // if s is 1 return p + if (s == 1) { + return p; + } + + // the accumulated product to return + BN254.G1Point memory acc = BN254.G1Point(0, 0); + // the 2^n*p to add to the accumulated product in each iteration + BN254.G1Point memory p2n = p; + // value of most significant bit + uint16 m = 1; + // index of most significant bit + uint8 i = 0; + + //loop until we reach the most significant bit + while (s >= m) { + unchecked { + // if the current bit is 1, add the 2^n*p to the accumulated product + if ((s >> i) & 1 == 1) { + acc = plus(acc, p2n); + } + // double the 2^n*p for the next iteration + p2n = plus(p2n, p2n); + + // increment the index and double the value of the most significant bit + m <<= 1; + ++i; + } + } + + // return the accumulated product + return acc; + } + + /** + * @return r the product of a point on G1 and a scalar, i.e. + * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all + * points p. + */ + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + require(success, "ec-mul-failed"); + } + + /** + * @return The result of computing the pairing check + * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + * For example, + * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. + */ + function pairing(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) + internal + view + returns (bool) + { + G1Point[2] memory p1 = [a1, b1]; + G2Point[2] memory p2 = [a2, b2]; + + uint256[12] memory input; + + for (uint256 i = 0; i < 2; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 8, input, mul(12, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + + require(success, "pairing-opcode-failed"); + + return out[0] != 0; + } + + /** + * @notice This function is functionally the same as pairing(), however it specifies a gas limit + * the user can set, as a precompile may use the entire gas budget if it reverts. + */ + function safePairing(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2, uint256 pairingGas) + internal + view + returns (bool, bool) + { + G1Point[2] memory p1 = [a1, b1]; + G2Point[2] memory p2 = [a2, b2]; + + uint256[12] memory input; + + for (uint256 i = 0; i < 2; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(pairingGas, 8, input, mul(12, 0x20), out, 0x20) + } + + //Out is the output of the pairing precompile, either 0 or 1 based on whether the two pairings are equal. + //Success is true if the precompile actually goes through (aka all inputs are valid) + + return (success, out[0] != 0); + } + + /// @return hashedG1 the keccak256 hash of the G1 Point + /// @dev used for BLS signatures + function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) { + assembly { + mstore(0, mload(pk)) + mstore(0x20, mload(add(0x20, pk))) + hashedG1 := keccak256(0, 0x40) + } + } + + /// @return the keccak256 hash of the G2 Point + /// @dev used for BLS signatures + function hashG2Point(BN254.G2Point memory pk) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1])); + } + + /** + * @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol + */ + function hashToG1(bytes32 _x) internal view returns (G1Point memory) { + uint256 beta = 0; + uint256 y = 0; + + uint256 x = uint256(_x) % FP_MODULUS; + + while (true) { + (beta, y) = findYFromX(x); + + // y^2 == beta + if (beta == mulmod(y, y, FP_MODULUS)) { + return G1Point(x, y); + } + + x = addmod(x, 1, FP_MODULUS); + } + return G1Point(0, 0); + } + + /** + * Given X, find Y + * + * where y = sqrt(x^3 + b) + * + * Returns: (x^3 + b), y + */ + function findYFromX(uint256 x) internal view returns (uint256, uint256) { + // beta = (x^3 + b) % p + uint256 beta = addmod(mulmod(mulmod(x, x, FP_MODULUS), x, FP_MODULUS), 3, FP_MODULUS); + + // y^2 = x^3 + b + // this acts like: y = sqrt(beta) = beta^((p+1) / 4) + uint256 y = expMod(beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS); + + return (beta, y); + } + + function expMod(uint256 _base, uint256 _exponent, uint256 _modulus) internal view returns (uint256 retval) { + bool success; + uint256[1] memory output; + uint256[6] memory input; + input[0] = 0x20; // baseLen = new(big.Int).SetBytes(getData(input, 0, 32)) + input[1] = 0x20; // expLen = new(big.Int).SetBytes(getData(input, 32, 32)) + input[2] = 0x20; // modLen = new(big.Int).SetBytes(getData(input, 64, 32)) + input[3] = _base; + input[4] = _exponent; + input[5] = _modulus; + assembly { + success := staticcall(sub(gas(), 2000), 5, input, 0xc0, output, 0x20) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + require(success, "BN254.expMod: call failure"); + return output[0]; + } +} diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index 6e8e766a..574bce66 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -11,13 +11,11 @@ import { DeployEverything } from "script/DeployEverything.s.sol"; import { ISignatureUtils } from "../../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; import { IStrategyManager } from "../../src/interface/EigenLayer-Slashing/IStrategyManager.sol"; import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IBLSApkRegistry, IRegistryCoordinator } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; import { IAVSDirectory } from "../../src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; -import { IRegistryCoordinatorExtended } from "../../src/interface/IRegistryCoordinatorExtended.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { BN254 } from "../../src/interface/libraries/BN254.sol"; interface Weth { function deposit() external payable; @@ -29,7 +27,6 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { using Strings for uint256; uint256[] privKeys; - IBLSApkRegistry.PubkeyRegistrationParams[] pubkeys; address EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY = 0x53012C69A189cfA2D9d29eb6F19B32e0A2EA3490; address EIGEN_DA_SERVICE_MANAGER = 0xD4A7E1Bd8015057293f0D0A557088c286942e84b; @@ -44,126 +41,32 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { pufferProtocol.createPufferModule(bytes32("SOME_MODULE_NAME")); } - function test_modify_operator() public { - vm.startPrank(DAO); - RestakingOperator operator = _createRestakingOperator(); - - address newDelegationApprover = makeAddr("newDelegationApprover"); - - vm.expectEmit(true, true, true, true); - emit IPufferModuleManager.RestakingOperatorModified(address(operator), newDelegationApprover); - moduleManager.callModifyOperatorDetails({ - restakingOperator: operator, - newDelegationApprover: newDelegationApprover - }); - - address result = operator.EIGEN_DELEGATION_MANAGER().delegationApprover(address(operator)); - assertEq(result, newDelegationApprover, "updated delegation approver"); - } - - function test_update_metadata_uri() public { - vm.startPrank(DAO); - RestakingOperator operator = _createRestakingOperator(); - - string memory newUri = "https://puffer.fi/updated.json"; - - vm.expectEmit(true, true, true, true); - emit IPufferModuleManager.RestakingOperatorMetadataURIUpdated(address(operator), newUri); - moduleManager.callUpdateMetadataURI(operator, newUri); - } - - // Don't remove this test, it is used as a reference for real registration - // function test_eigenda_avs() public { - // This test is for the Existing Holesky Testnet deployment - - // // vm.startPrank(DAO); - // // https://holesky.eigenlayer.xyz/operator/0xe2c2dc296a0bff351f6bc3e98d37ea798e393e56 - // address restakingOperator = 0xe2c2dc296a0bFF351F6bC3e98D37ea798e393e56; - // // RestakingOperator restakingOperator = _createRestakingOperator(); - - // // _depositToWETHEigenLayerStrategyAndDelegateTo(address(restakingOperator)); - - // IBLSApkRegistry.PubkeyRegistrationParams memory params = _generateBlsPubkeyParams(vm.envUint("OPERATOR_BLS_SK")); + // function test_modify_operator() public { + // vm.startPrank(DAO); + // RestakingOperator operator = _createRestakingOperator(); - // // He signs with his BLS key the message to register him with our Restaking Operator contract - // BN254.G1Point memory messageHash = IRegistryCoordinatorExtended(EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY) - // .pubkeyRegistrationMessageHash(address(restakingOperator)); + // address newDelegationApprover = makeAddr("newDelegationApprover"); - // params.pubkeyRegistrationSignature = BN254.scalar_mul(messageHash, vm.envUint("OPERATOR_BLS_SK")); + // vm.expectEmit(true, true, true, true); + // emit IPufferModuleManager.RestakingOperatorModified(address(operator), newDelegationApprover); + // moduleManager.callModifyOperatorDetails({ + // restakingOperator: operator, + // newDelegationApprover: newDelegationApprover + // }); - // (bytes32 digestHash, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) = - // _getOperatorSignature( - // vm.envUint("OPERATOR_ECDSA_SK"), - // address(restakingOperator), - // EIGEN_DA_SERVICE_MANAGER, - // bytes32(hex"aaaa"), - // type(uint256).max - // ); - - // address operatorAddress = vm.addr(vm.envUint("OPERATOR_ECDSA_SK")); - - // IPufferModuleManager pufferModuleManager = IPufferModuleManager(0xe4695ab93163F91665Ce5b96527408336f070a71); - - // vm.startPrank(0xDDDeAfB492752FC64220ddB3E7C9f1d5CcCdFdF0); - - // bytes memory hashCall = abi.encodeCall( - // IPufferModuleManager.updateAVSRegistrationSignatureProof, - // (RestakingOperator(restakingOperator), digestHash, operatorAddress) - // ); - - // //@todo has to be updated manually on the contract - // /// cast send 0xe4695ab93163F91665Ce5b96527408336f070a71 0xd82752c8000000000000000000000000e2c2dc296a0bff351f6bc3e98d37ea798e393e5653db2c4483378e8f37899e656561460562547c3f6a840a33f3c02f4c4f608eca000000000000000000000000454c063b1d5dfa8e0ebb4e4198cff1101dda5d2c --rpc-url=$HOLESKY_RPC_URL --private-key=$PUFFER_SHARED_PK - // console.log("hash call:"); - // console.logBytes(hashCall); - - // // pufferModuleManager.updateAVSRegistrationSignatureProof( - // // RestakingOperator(restakingOperator), digestHash, operatorAddress - // // ); - - // IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = - // new IRegistryCoordinator.OperatorKickParam[](1); - // operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ - // quorumNumber: 1, - // operator: 0xCE9AdA2dE1d94e62ca3574383a8F562B572dbC6C - // }); - // ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature; - // churnApproverSignature.signature = - // hex"9df0fa39818eec0a3a1dd9bb639dff0b98df88f82523d3f41d20611667681f83021adc47e5fe20e40de7650649b1385b8f14b824973a60eea95b560f98ce0ce81c"; - // churnApproverSignature.salt = hex"a930fd8d687e85d9957a4462b472e6a25d9f7c8b2fb639604da2f670a1f13512"; - // churnApproverSignature.expiry = 1712919092; - - // bytes memory calldataToRegister = abi.encodeCall( - // IPufferModuleManager.callRegisterOperatorToAVSWithChurn, - // ( - // RestakingOperator(restakingOperator), - // EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY, - // bytes(hex"01"), - // "20.64.16.29:32005;32004", - // params, - // operatorKickParams, - // churnApproverSignature, - // operatorSignature - // ) - // ); + // address result = operator.EIGEN_DELEGATION_MANAGER().delegationApprover(address(operator)); + // assertEq(result, newDelegationApprover, "updated delegation approver"); + // } - // console.log("Calldata to register operator to AVS, submit to PufferModuleManager:"); - // console.logBytes(calldataToRegister); + // function test_update_metadata_uri() public { + // vm.startPrank(DAO); + // RestakingOperator operator = _createRestakingOperator(); - // // Finish the registration - // (bool success,) = address(pufferModuleManager).call(calldataToRegister); - // assertEq(success, true, "register operator to avs"); + // string memory newUri = "https://puffer.fi/updated.json"; - // 4. Dao registers the operator by submitting his signature to the AVS - // pufferModuleManager.callRegisterOperatorToAVSWithChurn({ - // restakingOperator: RestakingOperator(restakingOperator), - // avsRegistryCoordinator: EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY, - // quorumNumbers: bytes(hex"01"), - // socket: "103.199.107.52:32005;32004", - // params: params, - // operatorSignature: operatorSignature, - // operatorKickParams: operatorKickParams, - // churnApproverSignature: churnApproverSignature - // }); + // vm.expectEmit(true, true, true, true); + // emit IPufferModuleManager.RestakingOperatorMetadataURIUpdated(address(operator), newUri); + // moduleManager.callUpdateMetadataURI(operator, newUri); // } function _depositToWETHEigenLayerStrategyAndDelegateTo(address restakingOperator) internal { @@ -200,27 +103,6 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { return operator; } - function _generateOperatorKeys() internal returns (uint256, IBLSApkRegistry.PubkeyRegistrationParams memory) { - uint256 privKey = uint256(keccak256(abi.encodePacked("secretPrivateKey"))); - - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; - pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); - pubkey.pubkeyG2 = _mulGo(privKey); - - return (privKey, pubkey); - } - - // Generates bls pubkey params from a private key - function _generateBlsPubkeyParams(uint256 privKey) - internal - returns (IBLSApkRegistry.PubkeyRegistrationParams memory) - { - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; - pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); - pubkey.pubkeyG2 = _mulGo(privKey); - return pubkey; - } - /** * @notice internal function for calculating a signature from the operator corresponding to `_operatorPrivateKey`, delegating them to * the `operator`, and expiring at `expiry`. diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol index a2854686..2de94fc3 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol @@ -4,19 +4,15 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/console.sol"; import { Test } from "forge-std/Test.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { DeployEverything } from "script/DeployEverything.s.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { PufferModule } from "../../src/PufferModule.sol"; -import { AVSContractsRegistry } from "../../src/AVSContractsRegistry.sol"; import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; -import { DeployEverything } from "script/DeployEverything.s.sol"; import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { ISignatureUtils } from "../../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; import { IAVSDirectory } from "../../src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; import { IRewardsCoordinator } from "../../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; -import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; +import { BN254 } from "../../src/interface/libraries/BN254.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; @@ -34,7 +30,6 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { using Strings for uint256; uint256[] privKeys; - IBLSApkRegistry.PubkeyRegistrationParams[] pubkeys; // https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#deployments IAVSDirectory public avsDirectory = IAVSDirectory(0x055733000064333CaDDbC92763c58BF0192fFeBf); @@ -57,91 +52,80 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { address RESTAKING_OPERATOR_BEACON = 0xa7DC88c059F57ADcE41070cEfEFd31F74649a261; address REWARDS_COORDINATOR = 0xAcc1fb458a1317E886dB376Fc8141540537E68fE; - function test_claim_undelegated_shares() public { - // On this block number, we have already undelegated shares from the operator on chain - // https://holesky.etherscan.io/tx/0x2d6675d7d71606a9aafcd9f0d8a65c8bad3d7c0ed7915bb67290e989a3c8f1c6#eventlog - vm.createSelectFork(vm.rpcUrl("holesky"), 1369706); - - PufferModuleManager pufferModuleManager = PufferModuleManager(PUFFER_MODULE_MANAGER); - - // Upgrade PufferModule to a new implementation, that fixes the issue - PufferModule upgrade = new PufferModule({ - protocol: PufferProtocol(payable(PUFFER_PROTOCOL_HOLESKY)), - eigenPodManager: EIGEN_POD_MANAGER, - delegationManager: IDelegationManager(DELEGATION_MANAGER), - moduleManager: pufferModuleManager, - rewardsCoordinator: IRewardsCoordinator(REWARDS_COORDINATOR) - }); - - // Execute Beacon upgrade - vm.startPrank(PUFFER_SHARED_DEV_WALLET); - AccessManager(ACCESS_MANAGER_HOLESKY).execute( - MODULE_BEACON_HOLESKY, abi.encodeCall(UpgradeableBeacon.upgradeTo, address(upgrade)) - ); - - // Upgrade PufferModuleManager to a new fixed implementation - PufferModuleManager moduleManagerImplementation = new PufferModuleManager({ - pufferModuleBeacon: MODULE_BEACON_HOLESKY, - restakingOperatorBeacon: RESTAKING_OPERATOR_BEACON, - pufferProtocol: PUFFER_PROTOCOL_HOLESKY - }); - - // Upgrade PufferModuleManager to a new implementation - UUPSUpgradeable(PUFFER_MODULE_MANAGER).upgradeToAndCall(address(moduleManagerImplementation), ""); - - // Withdrawal data can be fetched from the transaction logs - // cast run 0x2d6675d7d71606a9aafcd9f0d8a65c8bad3d7c0ed7915bb67290e989a3c8f1c6 --rpc-url=$HOLESKY_RPC_URL --verbose - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = IStrategy(BEACON_CHAIN_STRATEGY); - - uint256[] memory shares = new uint256[](1); - shares[0] = 224000000000000000000; - - IDelegationManagerTypes.Withdrawal[] memory withdrawals = new IDelegationManagerTypes.Withdrawal[](1); - withdrawals[0] = IDelegationManagerTypes.Withdrawal({ - staker: PUFFER_MODULE_0_HOLESKY, - delegatedTo: RESTAKING_OPERATOR_CONTRACT, - withdrawer: PUFFER_MODULE_0_HOLESKY, - nonce: 0, - startBlock: 1369340, - strategies: strategies, - scaledShares: shares - }); - - IERC20[] memory t = new IERC20[](1); - t[0] = IERC20(BEACON_CHAIN_STRATEGY); - - IERC20[][] memory tokens = new IERC20[][](1); - tokens[0] = t; - - uint256[] memory middlewareTimesIndexes = new uint256[](1); // 0 - bool[] memory receiveAsTokens = new bool[](1); // false - - // At the moment the caller is the admin role, but this will be restricted to the PufferPaymaster - pufferModuleManager.callCompleteQueuedWithdrawals({ - moduleName: bytes32("PUFFER_MODULE_0"), - withdrawals: withdrawals, - tokens: tokens, - receiveAsTokens: receiveAsTokens - }); - - ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry; - // Delegate again to the same operator - pufferModuleManager.callDelegateTo( - bytes32("PUFFER_MODULE_0"), RESTAKING_OPERATOR_CONTRACT, signatureWithExpiry, bytes32(0) - ); - } - - // Generates bls pubkey params from a private key - function _generateBlsPubkeyParams(uint256 privKey) - internal - returns (IBLSApkRegistry.PubkeyRegistrationParams memory) - { - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; - pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); - pubkey.pubkeyG2 = _mulGo(privKey); - return pubkey; - } + // function test_claim_undelegated_shares() public { + // // On this block number, we have already undelegated shares from the operator on chain + // // https://holesky.etherscan.io/tx/0x2d6675d7d71606a9aafcd9f0d8a65c8bad3d7c0ed7915bb67290e989a3c8f1c6#eventlog + // vm.createSelectFork(vm.rpcUrl("holesky"), 1369706); + + // PufferModuleManager pufferModuleManager = PufferModuleManager(PUFFER_MODULE_MANAGER); + + // // Upgrade PufferModule to a new implementation, that fixes the issue + // PufferModule upgrade = new PufferModule({ + // protocol: PufferProtocol(payable(PUFFER_PROTOCOL_HOLESKY)), + // eigenPodManager: EIGEN_POD_MANAGER, + // delegationManager: IDelegationManager(DELEGATION_MANAGER), + // moduleManager: pufferModuleManager, + // rewardsCoordinator: IRewardsCoordinator(REWARDS_COORDINATOR) + // }); + + // // Execute Beacon upgrade + // vm.startPrank(PUFFER_SHARED_DEV_WALLET); + // AccessManager(ACCESS_MANAGER_HOLESKY).execute( + // MODULE_BEACON_HOLESKY, abi.encodeCall(UpgradeableBeacon.upgradeTo, address(upgrade)) + // ); + + // // Upgrade PufferModuleManager to a new fixed implementation + // PufferModuleManager moduleManagerImplementation = new PufferModuleManager({ + // pufferModuleBeacon: MODULE_BEACON_HOLESKY, + // restakingOperatorBeacon: RESTAKING_OPERATOR_BEACON, + // pufferProtocol: PUFFER_PROTOCOL_HOLESKY + // }); + + // // Upgrade PufferModuleManager to a new implementation + // UUPSUpgradeable(PUFFER_MODULE_MANAGER).upgradeToAndCall(address(moduleManagerImplementation), ""); + + // // Withdrawal data can be fetched from the transaction logs + // // cast run 0x2d6675d7d71606a9aafcd9f0d8a65c8bad3d7c0ed7915bb67290e989a3c8f1c6 --rpc-url=$HOLESKY_RPC_URL --verbose + // IStrategy[] memory strategies = new IStrategy[](1); + // strategies[0] = IStrategy(BEACON_CHAIN_STRATEGY); + + // uint256[] memory shares = new uint256[](1); + // shares[0] = 224000000000000000000; + + // IDelegationManagerTypes.Withdrawal[] memory withdrawals = new IDelegationManagerTypes.Withdrawal[](1); + // withdrawals[0] = IDelegationManagerTypes.Withdrawal({ + // staker: PUFFER_MODULE_0_HOLESKY, + // delegatedTo: RESTAKING_OPERATOR_CONTRACT, + // withdrawer: PUFFER_MODULE_0_HOLESKY, + // nonce: 0, + // startBlock: 1369340, + // strategies: strategies, + // scaledShares: shares + // }); + + // IERC20[] memory t = new IERC20[](1); + // t[0] = IERC20(BEACON_CHAIN_STRATEGY); + + // IERC20[][] memory tokens = new IERC20[][](1); + // tokens[0] = t; + + // uint256[] memory middlewareTimesIndexes = new uint256[](1); // 0 + // bool[] memory receiveAsTokens = new bool[](1); // false + + // // At the moment the caller is the admin role, but this will be restricted to the PufferPaymaster + // pufferModuleManager.callCompleteQueuedWithdrawals({ + // moduleName: bytes32("PUFFER_MODULE_0"), + // withdrawals: withdrawals, + // tokens: tokens, + // receiveAsTokens: receiveAsTokens + // }); + + // ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry; + // // Delegate again to the same operator + // pufferModuleManager.callDelegateTo( + // bytes32("PUFFER_MODULE_0"), RESTAKING_OPERATOR_CONTRACT, signatureWithExpiry, bytes32(0) + // ); + // } function _mulGo(uint256 x) internal returns (BN254.G2Point memory g2Point) { string[] memory inputs = new string[](3); diff --git a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol index d86e3752..d5e09e9b 100644 --- a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol +++ b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol @@ -3,19 +3,12 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/console.sol"; import { Test } from "forge-std/Test.sol"; -import { DeployEverything } from "script/DeployEverything.s.sol"; -import { IPufferModuleManager } from "src/interface/IPufferModuleManager.sol"; -import { PufferModuleManager } from "src/PufferModuleManager.sol"; -import { DeployEverything } from "script/DeployEverything.s.sol"; import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; import { ISignatureUtils } from "src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; -import { IBLSApkRegistry } from "eigenlayer-middleware/interfaces/IRegistryCoordinator.sol"; import { IAVSDirectory } from "src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; -import { IRegistryCoordinatorExtended } from "src/interface/IRegistryCoordinatorExtended.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { RestakingOperator } from "src/RestakingOperator.sol"; +import { BN254 } from "src/interface/libraries/BN254.sol"; interface Weth { function deposit() external payable; @@ -28,7 +21,6 @@ contract PufferModuleManagerHoleskyTestnetFFI is Test { using Strings for uint256; uint256[] privKeys; - IBLSApkRegistry.PubkeyRegistrationParams[] pubkeys; // https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#deployments IAVSDirectory public avsDirectory = IAVSDirectory(0x055733000064333CaDDbC92763c58BF0192fFeBf); @@ -51,17 +43,6 @@ contract PufferModuleManagerHoleskyTestnetFFI is Test { address RESTAKING_OPERATOR_BEACON = 0xa7DC88c059F57ADcE41070cEfEFd31F74649a261; address REWARDS_COORDINATOR = 0xAcc1fb458a1317E886dB376Fc8141540537E68fE; - // Generates bls pubkey params from a private key - function _generateBlsPubkeyParams(uint256 privKey) - internal - returns (IBLSApkRegistry.PubkeyRegistrationParams memory) - { - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; - pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); - pubkey.pubkeyG2 = _mulGo(privKey); - return pubkey; - } - function _mulGo(uint256 x) internal returns (BN254.G2Point memory g2Point) { string[] memory inputs = new string[](3); inputs[0] = "./test/helpers/go2mul"; // lib/eigenlayer-middleware/test/ffi/go/g2mul.go binary From c71d469e9fae0213e79746617dae715c5d959ae2 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 13 Dec 2024 13:42:05 +0100 Subject: [PATCH 12/37] lowercase import --- .../script/CompleteQueuedWithdrawals.s.sol | 6 +++--- mainnet-contracts/script/DeployPufETH.s.sol | 4 ++-- mainnet-contracts/script/DeployPuffer.s.sol | 6 +++--- .../script/DeployPufferModuleImplementation.s.sol | 4 ++-- mainnet-contracts/script/DeployPufferVaultV3.s.sol | 6 +++--- .../script/DeployRestakingOperator.s.sol | 6 +++--- mainnet-contracts/script/UpgradePufETH.s.sol | 6 +++--- mainnet-contracts/script/UpgradePufferModule.s.sol | 4 ++-- mainnet-contracts/src/PufferModule.sol | 14 +++++++------- mainnet-contracts/src/PufferModuleManager.sol | 8 ++++---- mainnet-contracts/src/PufferVault.sol | 4 ++-- mainnet-contracts/src/PufferVaultV2.sol | 6 +++--- mainnet-contracts/src/PufferVaultV3.sol | 6 +++--- mainnet-contracts/src/PufferVaultV4.sol | 6 +++--- mainnet-contracts/src/RestakingOperator.sol | 6 +++--- mainnet-contracts/src/interface/IPufferModule.sol | 8 ++++---- mainnet-contracts/src/struct/ModuleStorage.sol | 2 +- .../Integration/PufferRevenueDepositor.fork.t.sol | 6 +++--- .../test/Integration/PufferTest.integration.t.sol | 8 ++++---- .../PufferVaultV2WithdrawFromEl.fork.t.sol | 6 +++--- mainnet-contracts/test/MainnetForkTestHelper.sol | 6 +++--- .../PufferModuleManager.integration.t.sol | 10 +++++----- ...duleManagerHoleskyTestnetTest.integration.t.sol | 12 ++++++------ .../ffi/PufferModuleManagerHoleskyFfi.t.sol | 8 ++++---- .../test/mocks/DelegationManagerMock.sol | 8 ++++---- .../test/mocks/EigenLayerDelegationManagerMock.sol | 4 ++-- .../test/mocks/EigenLayerManagerMock.sol | 4 ++-- .../test/mocks/EigenPodManagerMock.sol | 4 ++-- mainnet-contracts/test/mocks/PausableMock.sol | 2 +- .../test/mocks/PufferVaultV2Tests.sol | 6 +++--- .../test/mocks/PufferVaultV4Tests.sol | 6 +++--- mainnet-contracts/test/mocks/SlasherMock.sol | 6 +++--- mainnet-contracts/test/mocks/stETHStrategyMock.sol | 2 +- .../test/mocks/stETHStrategyTestnet.sol | 2 +- .../test/unit/PufferModuleManager.t.sol | 4 ++-- 35 files changed, 103 insertions(+), 103 deletions(-) diff --git a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol index 717b83a9..7db45c08 100644 --- a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol +++ b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { Script } from "forge-std/Script.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; -import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IDelegationManagerTypes } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; struct ScriptParameters { address pufferModuleManager; diff --git a/mainnet-contracts/script/DeployPufETH.s.sol b/mainnet-contracts/script/DeployPufETH.s.sol index 60fe5c56..996231aa 100644 --- a/mainnet-contracts/script/DeployPufETH.s.sol +++ b/mainnet-contracts/script/DeployPufETH.s.sol @@ -10,8 +10,8 @@ import { PufferVault } from "../src/PufferVault.sol"; import { Timelock } from "../src/Timelock.sol"; import { NoImplementation } from "../src/NoImplementation.sol"; import { PufferDeployment } from "../src/structs/PufferDeployment.sol"; -import { IEigenLayer } from "../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { stETHMock } from "../test/mocks/stETHMock.sol"; diff --git a/mainnet-contracts/script/DeployPuffer.s.sol b/mainnet-contracts/script/DeployPuffer.s.sol index 95030a90..a98c46a6 100644 --- a/mainnet-contracts/script/DeployPuffer.s.sol +++ b/mainnet-contracts/script/DeployPuffer.s.sol @@ -13,8 +13,8 @@ import { stdJson } from "forge-std/StdJson.sol"; import { EigenPodManagerMock } from "../test/mocks/EigenPodManagerMock.sol"; import { DelegationManagerMock } from "../test/mocks/DelegationManagerMock.sol"; import { BeaconMock } from "../test/mocks/BeaconMock.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; @@ -24,7 +24,7 @@ import { ValidatorTicketPricer } from "../src/ValidatorTicketPricer.sol"; import { OperationsCoordinator } from "../src/OperationsCoordinator.sol"; import { PufferOracleV2 } from "../src/PufferOracleV2.sol"; import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { AVSContractsRegistry } from "../src/AVSContractsRegistry.sol"; import { RewardsCoordinatorMock } from "../test/mocks/RewardsCoordinatorMock.sol"; diff --git a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol index 05395e88..61b4fcc1 100644 --- a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol +++ b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol @@ -12,9 +12,9 @@ import { stdJson } from "forge-std/StdJson.sol"; import { GuardianModule } from "../src/GuardianModule.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { PufferModule } from "../src/PufferModule.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { IRewardsCoordinator } from "src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; /** diff --git a/mainnet-contracts/script/DeployPufferVaultV3.s.sol b/mainnet-contracts/script/DeployPufferVaultV3.s.sol index 4878f140..9df636e1 100644 --- a/mainnet-contracts/script/DeployPufferVaultV3.s.sol +++ b/mainnet-contracts/script/DeployPufferVaultV3.s.sol @@ -9,10 +9,10 @@ import { PufferVaultV3 } from "src/PufferVaultV3.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; -import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; /** * @title DeployPufferVaultV3 diff --git a/mainnet-contracts/script/DeployRestakingOperator.s.sol b/mainnet-contracts/script/DeployRestakingOperator.s.sol index 83d51286..b53f44a3 100644 --- a/mainnet-contracts/script/DeployRestakingOperator.s.sol +++ b/mainnet-contracts/script/DeployRestakingOperator.s.sol @@ -6,10 +6,10 @@ import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { RestakingOperator } from "../src/RestakingOperator.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; /** diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index a5f47cd2..a77742d6 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -7,9 +7,9 @@ import { PufferVault } from "../src/PufferVault.sol"; import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; -import { IEigenLayer } from "../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { LidoWithdrawalQueueMock } from "../test/mocks/LidoWithdrawalQueueMock.sol"; diff --git a/mainnet-contracts/script/UpgradePufferModule.s.sol b/mainnet-contracts/script/UpgradePufferModule.s.sol index c34c59f9..28a472bc 100644 --- a/mainnet-contracts/script/UpgradePufferModule.s.sol +++ b/mainnet-contracts/script/UpgradePufferModule.s.sol @@ -10,8 +10,8 @@ import { BaseScript } from "script/BaseScript.s.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { PufferModule } from "../src/PufferModule.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IRewardsCoordinator } from "../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; /** * @title UpgradePufferModule diff --git a/mainnet-contracts/src/PufferModule.sol b/mainnet-contracts/src/PufferModule.sol index eadced6d..f167e7ab 100644 --- a/mainnet-contracts/src/PufferModule.sol +++ b/mainnet-contracts/src/PufferModule.sol @@ -3,12 +3,12 @@ pragma solidity >=0.8.0 <0.9.0; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IEigenPodManager } from "../src/interface/EigenLayer-Slashing/IEigenPodManager.sol"; -import { ISignatureUtils } from "../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; -import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IEigenPodManager } from "../src/interface/Eigenlayer-Slashing/IEigenPodManager.sol"; +import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; -import { IEigenPod } from "../src/interface/EigenLayer-Slashing/IEigenPod.sol"; +import { IEigenPod } from "../src/interface/Eigenlayer-Slashing/IEigenPod.sol"; import { PufferModuleManager } from "./PufferModuleManager.sol"; import { IPufferModule } from "./interface/IPufferModule.sol"; import { Unauthorized } from "./Errors.sol"; @@ -16,8 +16,8 @@ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/I import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ModuleStorage } from "./struct/ModuleStorage.sol"; -import { IDelegationManagerTypes } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IRewardsCoordinator } from "src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IDelegationManagerTypes } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IRewardsCoordinator } from "src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; /** * @title PufferModule diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 912258a9..c71bcc4c 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -14,12 +14,12 @@ import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol"; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IDelegationManagerTypes } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { RestakingOperator } from "./RestakingOperator.sol"; -import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; /** * @title PufferModuleManager diff --git a/mainnet-contracts/src/PufferVault.sol b/mainnet-contracts/src/PufferVault.sol index 9f84b2f7..58809642 100644 --- a/mainnet-contracts/src/PufferVault.sol +++ b/mainnet-contracts/src/PufferVault.sol @@ -5,8 +5,8 @@ import { IPufferVault } from "./interface/IPufferVault.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; import { PufferVaultStorage } from "./PufferVaultStorage.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; diff --git a/mainnet-contracts/src/PufferVaultV2.sol b/mainnet-contracts/src/PufferVaultV2.sol index 32625e3a..ad885b0b 100644 --- a/mainnet-contracts/src/PufferVaultV2.sol +++ b/mainnet-contracts/src/PufferVaultV2.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVault } from "./PufferVault.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV2 } from "./interface/IPufferVaultV2.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; diff --git a/mainnet-contracts/src/PufferVaultV3.sol b/mainnet-contracts/src/PufferVaultV3.sol index 04e8c3c5..db0a2fc4 100644 --- a/mainnet-contracts/src/PufferVaultV3.sol +++ b/mainnet-contracts/src/PufferVaultV3.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "./PufferVaultV2.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; diff --git a/mainnet-contracts/src/PufferVaultV4.sol b/mainnet-contracts/src/PufferVaultV4.sol index 3b47ef4e..474f1f0e 100644 --- a/mainnet-contracts/src/PufferVaultV4.sol +++ b/mainnet-contracts/src/PufferVaultV4.sol @@ -3,9 +3,9 @@ pragma solidity >=0.8.0 <0.9.0; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/EigenLayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; import { PufferVaultV3 } from "./PufferVaultV3.sol"; diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index e66a4fa1..e1211616 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -4,14 +4,14 @@ pragma solidity >=0.8.0 <0.9.0; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IAllocationManager } from "../src/interface/EigenLayer-Slashing/IAllocationManager.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; import { Unauthorized, InvalidAddress } from "./Errors.sol"; import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import { IRewardsCoordinator } from "./interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IRewardsCoordinator } from "./interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; /** * @title RestakingOperator diff --git a/mainnet-contracts/src/interface/IPufferModule.sol b/mainnet-contracts/src/interface/IPufferModule.sol index 00dd7297..4d72e9a3 100644 --- a/mainnet-contracts/src/interface/IPufferModule.sol +++ b/mainnet-contracts/src/interface/IPufferModule.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { ISignatureUtils } from "../interface/EigenLayer-Slashing/ISignatureUtils.sol"; -// import { BeaconChainProofs } from "../interface/EigenLayer-Slashing/BeaconChainProofs.sol"; -import { IDelegationManager } from "../interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +// import { BeaconChainProofs } from "../interface/Eigenlayer-Slashing/BeaconChainProofs.sol"; +import { IDelegationManager } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IDelegationManagerTypes } from "../interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; /** * @title IPufferModule diff --git a/mainnet-contracts/src/struct/ModuleStorage.sol b/mainnet-contracts/src/struct/ModuleStorage.sol index 319c572f..c1d0c352 100644 --- a/mainnet-contracts/src/struct/ModuleStorage.sol +++ b/mainnet-contracts/src/struct/ModuleStorage.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IEigenPod } from "../interface/EigenLayer-Slashing/IEigenPod.sol"; +import { IEigenPod } from "../interface/Eigenlayer-Slashing/IEigenPod.sol"; /** * @custom:storage-location erc7201:PufferModule.storage diff --git a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol index ddd34c02..f88ba029 100644 --- a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol @@ -12,10 +12,10 @@ import { PufferVaultV4 } from "../../src/PufferVaultV4.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; struct AssetValue { IERC20 asset; diff --git a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol index 80b42516..a0d26c7c 100644 --- a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol +++ b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol @@ -9,7 +9,7 @@ import { PufferVaultV2Tests } from "../../test/mocks/PufferVaultV2Tests.sol"; import { PufferDepositorV2 } from "../../src/PufferDepositorV2.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { MockPufferOracle } from "../mocks/MockPufferOracle.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IPufferVault } from "../../src/interface/IPufferVault.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -22,13 +22,13 @@ import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessMana import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { IWstETH } from "../../src/interface/Lido/IWstETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { Timelock } from "../../src/Timelock.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; import { Permit } from "../../src/structs/Permit.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; /** * @dev PufferDepositor and PufferVault tests (v1) diff --git a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol index b60f4108..ef60f431 100644 --- a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol @@ -6,9 +6,9 @@ import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; diff --git a/mainnet-contracts/test/MainnetForkTestHelper.sol b/mainnet-contracts/test/MainnetForkTestHelper.sol index ac39ac3c..5ea810d6 100644 --- a/mainnet-contracts/test/MainnetForkTestHelper.sol +++ b/mainnet-contracts/test/MainnetForkTestHelper.sol @@ -10,7 +10,7 @@ import { PufferVaultV2Tests } from "../test/mocks/PufferVaultV2Tests.sol"; import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; import { PufferDepositorV2 } from "../src/PufferDepositorV2.sol"; import { MockPufferOracle } from "./mocks/MockPufferOracle.sol"; -import { IEigenLayer } from "../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol"; @@ -19,13 +19,13 @@ import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { IPufferOracle } from "../src/interface/IPufferOracle.sol"; import { IWstETH } from "../src/interface/Lido/IWstETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { Timelock } from "../src/Timelock.sol"; import { IWETH } from "../src/interface/Other/IWETH.sol"; import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; import { Permit } from "../src/structs/Permit.sol"; import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; -import { IDelegationManager } from "../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { DeployerHelper } from "../script/DeployerHelper.s.sol"; import { IPufferRevenueDepositor } from "../src/interface/IPufferRevenueDepositor.sol"; diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index 574bce66..60b14086 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -8,11 +8,11 @@ import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.sol"; import { RestakingOperator } from "../../src/RestakingOperator.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; -import { ISignatureUtils } from "../../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; -import { IStrategyManager } from "../../src/interface/EigenLayer-Slashing/IStrategyManager.sol"; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IAVSDirectory } from "../../src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategyManager } from "../../src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { BN254 } from "../../src/interface/libraries/BN254.sol"; diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol index 2de94fc3..eae878d9 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol @@ -7,17 +7,17 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { PufferModule } from "../../src/PufferModule.sol"; import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "../../src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IAVSDirectory } from "../../src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; -import { IRewardsCoordinator } from "../../src/interface/EigenLayer-Slashing/IRewardsCoordinator.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; +import { IRewardsCoordinator } from "../../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { BN254 } from "../../src/interface/libraries/BN254.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; -import { IDelegationManagerTypes } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; interface Weth { function deposit() external payable; diff --git a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol index d5e09e9b..2615533b 100644 --- a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol +++ b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol @@ -3,10 +3,10 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/console.sol"; import { Test } from "forge-std/Test.sol"; -import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; -import { IAVSDirectory } from "src/interface/EigenLayer-Slashing/IAVSDirectory.sol"; -import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IAVSDirectory } from "src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { BN254 } from "src/interface/libraries/BN254.sol"; diff --git a/mainnet-contracts/test/mocks/DelegationManagerMock.sol b/mainnet-contracts/test/mocks/DelegationManagerMock.sol index d9a74df4..4f88b0b9 100644 --- a/mainnet-contracts/test/mocks/DelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/DelegationManagerMock.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { ISignatureUtils } from "src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; -import { IStrategyManager } from "src/interface/EigenLayer-Slashing/IStrategyManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; +import { IStrategyManager } from "src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract DelegationManagerMock { diff --git a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol index 30440d9d..a498c525 100644 --- a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "../../src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; contract EigenLayerDelegationManagerMock is IDelegationManager { /** diff --git a/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol index 99e2801f..0faf9f7c 100644 --- a/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenLayerManagerMock.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IEigenLayer } from "../../src/interface/EigenLayer-Slashing/IEigenLayer.sol"; +import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; contract EigenLayerManagerMock is IEigenLayer { function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares) { } diff --git a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol index ae36d8ae..67312086 100644 --- a/mainnet-contracts/test/mocks/EigenPodManagerMock.sol +++ b/mainnet-contracts/test/mocks/EigenPodManagerMock.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Test.sol"; -import "src/interface/EigenLayer-Slashing/IEigenPodManager.sol"; -import "src/interface/EigenLayer-Slashing/IAllocationManager.sol"; +import "src/interface/Eigenlayer-Slashing/IEigenPodManager.sol"; +import "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; contract EigenPodMock { function startCheckpoint(bool) external { } diff --git a/mainnet-contracts/test/mocks/PausableMock.sol b/mainnet-contracts/test/mocks/PausableMock.sol index 3adf92fb..d8f5b17e 100644 --- a/mainnet-contracts/test/mocks/PausableMock.sol +++ b/mainnet-contracts/test/mocks/PausableMock.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.12; -import "src/interface/EigenLayer-Slashing/IPausable.sol"; +import "src/interface/Eigenlayer-Slashing/IPausable.sol"; /** * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract. diff --git a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol index 64ba6134..a23c9aff 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "src/PufferVaultV2.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; contract PufferVaultV2Tests is PufferVaultV2 { constructor( diff --git a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol index 3ab4d16f..e949f9e8 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV4 } from "src/PufferVaultV4.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/EigenLayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IPufferRevenueDepositor } from "src/interface/IPufferRevenueDepositor.sol"; contract PufferVaultV4Tests is PufferVaultV4 { diff --git a/mainnet-contracts/test/mocks/SlasherMock.sol b/mainnet-contracts/test/mocks/SlasherMock.sol index 9cea506e..09a2b9fd 100644 --- a/mainnet-contracts/test/mocks/SlasherMock.sol +++ b/mainnet-contracts/test/mocks/SlasherMock.sol @@ -1,9 +1,9 @@ // // SPDX-License-Identifier: BUSL-1.1 // pragma solidity >=0.8.12; -// import "src/interface/EigenLayer-Slashing/IAllocationManager.sol"; -// import "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; -// import "src/interface/EigenLayer-Slashing/IStrategyManager.sol"; +// import "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +// import "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +// import "src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; // import "test/mocks/StructuredLinkedListMock.sol"; // import "test/mocks/PausableMock.sol"; // import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; diff --git a/mainnet-contracts/test/mocks/stETHStrategyMock.sol b/mainnet-contracts/test/mocks/stETHStrategyMock.sol index e794d4f6..9a55fc59 100644 --- a/mainnet-contracts/test/mocks/stETHStrategyMock.sol +++ b/mainnet-contracts/test/mocks/stETHStrategyMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; contract stETHStrategyMock is IStrategy { /** diff --git a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol b/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol index 514e7d6e..a86f4497 100644 --- a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol +++ b/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IStrategy } from "../../src/interface/EigenLayer-Slashing/IStrategy.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; contract stETHStrategyTestnet is IStrategy { /** diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index 8e5c112c..4fe6a6e4 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -9,11 +9,11 @@ import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.s import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { Merkle } from "murky/Merkle.sol"; -import { ISignatureUtils } from "src/interface/EigenLayer-Slashing/ISignatureUtils.sol"; +import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { Unauthorized } from "../../src/Errors.sol"; import { ROLE_ID_OPERATIONS_PAYMASTER } from "../../script/Roles.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IDelegationManager } from "src/interface/EigenLayer-Slashing/IDelegationManager.sol"; +import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { RestakingOperator } from "src/RestakingOperator.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; From f4d8503d41b66966c4d39085136284ad39b4f8c1 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 13 Dec 2024 13:51:34 +0100 Subject: [PATCH 13/37] exclude EL lib from the coverage --- .github/workflows/mainnet-contracts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mainnet-contracts.yml b/.github/workflows/mainnet-contracts.yml index 209350b5..7bfd4957 100644 --- a/.github/workflows/mainnet-contracts.yml +++ b/.github/workflows/mainnet-contracts.yml @@ -101,7 +101,7 @@ jobs: - name: "Generate the coverage report" working-directory: mainnet-contracts - run: 'forge coverage --no-match-coverage "(script|test|mock|node_modules|integrations|echidna|L1RewardManagerUnsafe)" --no-match-contract "PufferModuleManagerHoleskyTestnetFFI" --report lcov -vvv' + run: 'forge coverage --no-match-coverage "(script|test|mock|node_modules|interface|integrations|echidna|L1RewardManagerUnsafe)" --no-match-contract "PufferModuleManagerHoleskyTestnetFFI" --report lcov -vvv' env: ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }} HOLESKY_RPC_URL: ${{ secrets.HOLESKY_RPC_URL }} From 64cf8165ded76f97734f10bb9aca90bb9ac73852 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 16 Dec 2024 18:02:02 +0100 Subject: [PATCH 14/37] more weight loss --- mainnet-contracts/docs/PufferVaultV2.md | 2 +- mainnet-contracts/script/DeployPufETH.s.sol | 9 +- .../script/DeployPufferVaultV3.s.sol | 8 +- mainnet-contracts/script/UpgradePufETH.s.sol | 5 +- mainnet-contracts/src/PufferVault.sol | 129 +--- mainnet-contracts/src/PufferVaultStorage.sol | 6 +- mainnet-contracts/src/PufferVaultV2.sol | 37 +- mainnet-contracts/src/PufferVaultV3.sol | 18 +- mainnet-contracts/src/PufferVaultV4.sol | 11 +- .../src/echidna/EchidnaPufferVaultV2.sol | 9 +- .../PufferRevenueDepositor.fork.t.sol | 3 - .../Integration/PufferTest.integration.t.sol | 166 +---- .../test/Integration/PufferVaultV2.fork.t.sol | 1 - .../PufferVaultV2WithdrawFromEl.fork.t.sol | 13 +- .../test/MainnetForkTestHelper.sol | 13 +- .../test/mocks/DelegationManagerMock.sol | 16 - .../test/mocks/PufferVaultV2Tests.sol | 15 +- .../test/mocks/PufferVaultV4Tests.sol | 19 +- mainnet-contracts/test/mocks/SlasherMock.sol | 615 ------------------ mainnet-contracts/test/unit/PufETH.t.sol | 13 - .../test/unit/PufferModuleManager.t.sol | 37 +- mainnet-contracts/test/unit/Timelock.t.sol | 9 - 22 files changed, 66 insertions(+), 1088 deletions(-) delete mode 100644 mainnet-contracts/test/mocks/SlasherMock.sol diff --git a/mainnet-contracts/docs/PufferVaultV2.md b/mainnet-contracts/docs/PufferVaultV2.md index 1d147b94..c195471c 100644 --- a/mainnet-contracts/docs/PufferVaultV2.md +++ b/mainnet-contracts/docs/PufferVaultV2.md @@ -37,7 +37,7 @@ The PufferVault maintains the addresses of important contracts related to EigenL * `uint256 lidoLockedETH`: The amount of ETH the Puffer Protocol has locked inside of Lido * `uint256 eigenLayerPendingWithdrawalSharesAmount`: The amount of stETH shares the Puffer vault has pending for withdrawal from EigenLayer -* `bool isLidoWithdrawal`: Deprecated from PufferVault version 1 +* `bool deprecated_isLidoWithdrawal`: Deprecated from PufferVault version 1 * `EnumerableSet.UintSet lidoWithdrawals`: Deprecated from PufferVault version 1 * `EnumerableSet.Bytes32Set eigenLayerWithdrawals`: Tracks withdrawalRoots from EigenLayer withdrawals * `EnumerableMap.UintToUintMap lidoWithdrawalAmounts`: Tracks the amounts of corresponding to each Lido withdrawal diff --git a/mainnet-contracts/script/DeployPufETH.s.sol b/mainnet-contracts/script/DeployPufETH.s.sol index 996231aa..babda705 100644 --- a/mainnet-contracts/script/DeployPufETH.s.sol +++ b/mainnet-contracts/script/DeployPufETH.s.sol @@ -108,8 +108,7 @@ contract DeployPufETH is BaseScript { wethAddress = address(weth); // Deploy implementation contracts - pufferVaultImplementation = - new PufferVault(IStETH(stETHAddress), lidoWithdrawalQueue, stETHStrategy, eigenStrategyManager); + pufferVaultImplementation = new PufferVault(IStETH(stETHAddress), lidoWithdrawalQueue); vm.label(address(pufferVaultImplementation), "PufferVaultImplementation"); pufferDepositorImplementation = new PufferDepositor({ stETH: IStETH(stETHAddress), pufferVault: PufferVault(payable(vaultProxy)) }); @@ -203,10 +202,8 @@ contract DeployPufETH is BaseScript { function _setupOther() internal view returns (bytes[] memory) { bytes[] memory calldatas = new bytes[](5); - bytes4[] memory selectors = new bytes4[](3); - selectors[0] = PufferVault.depositToEigenLayer.selector; - selectors[1] = PufferVault.initiateETHWithdrawalsFromLido.selector; - selectors[2] = PufferVault.initiateStETHWithdrawalFromEigenLayer.selector; + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = PufferVault.initiateETHWithdrawalsFromLido.selector; // Setup setup role calldatas[0] = abi.encodeWithSelector( diff --git a/mainnet-contracts/script/DeployPufferVaultV3.s.sol b/mainnet-contracts/script/DeployPufferVaultV3.s.sol index 9df636e1..8d260e0c 100644 --- a/mainnet-contracts/script/DeployPufferVaultV3.s.sol +++ b/mainnet-contracts/script/DeployPufferVaultV3.s.sol @@ -9,10 +9,7 @@ import { PufferVaultV3 } from "src/PufferVaultV3.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; -import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; /** * @title DeployPufferVaultV3 @@ -34,10 +31,7 @@ contract DeployPufferVaultV3 is DeployerHelper { stETH: IStETH(_getStETH()), weth: IWETH(_getWETH()), lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - stETHStrategy: IStrategy(_getStETHStrategy()), - eigenStrategyManager: IEigenLayer(_getEigenLayerStrategyManager()), - oracle: IPufferOracle(_getPufferOracle()), - delegationManager: IDelegationManager(_getEigenDelegationManager()) + oracle: IPufferOracle(_getPufferOracle()) }); //@todo Double check reinitialization diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index a77742d6..204b214f 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -61,14 +61,11 @@ contract UpgradePufETH is BaseScript { //@todo this is for tests only AccessManager(deployment.accessManager).grantRole(1, _broadcaster, 0); - PufferVaultV3 newImplementation = new PufferVaultV4Tests( + PufferVaultV4Tests newImplementation = new PufferVaultV4Tests( IStETH(deployment.stETH), IWETH(deployment.weth), ILidoWithdrawalQueue(deployment.lidoWithdrawalQueueMock), - IStrategy(deployment.stETHStrategyMock), - IEigenLayer(deployment.eigenStrategyManagerMock), IPufferOracle(pufferOracle), - _DELEGATION_MANAGER, IPufferRevenueDepositor(revenueDepositor) ); diff --git a/mainnet-contracts/src/PufferVault.sol b/mainnet-contracts/src/PufferVault.sol index 58809642..fd62478a 100644 --- a/mainnet-contracts/src/PufferVault.sol +++ b/mainnet-contracts/src/PufferVault.sol @@ -2,11 +2,8 @@ pragma solidity >=0.8.0 <0.9.0; import { IPufferVault } from "./interface/IPufferVault.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; import { PufferVaultStorage } from "./PufferVaultStorage.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; @@ -37,18 +34,6 @@ contract PufferVault is using EnumerableSet for EnumerableSet.UintSet; using SafeERC20 for address; - /** - * @dev EigenLayer stETH strategy - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ - IStrategy internal immutable _EIGEN_STETH_STRATEGY; - - /** - * @dev EigenLayer Strategy Manager - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ - IEigenLayer internal immutable _EIGEN_STRATEGY_MANAGER; - /** * @dev stETH contract * @custom:oz-upgrades-unsafe-allow state-variable-immutable @@ -64,16 +49,9 @@ contract PufferVault is /** * @custom:oz-upgrades-unsafe-allow constructor */ - constructor( - IStETH stETH, - ILidoWithdrawalQueue lidoWithdrawalQueue, - IStrategy stETHStrategy, - IEigenLayer eigenStrategyManager - ) payable { + constructor(IStETH stETH, ILidoWithdrawalQueue lidoWithdrawalQueue) payable { _ST_ETH = stETH; _LIDO_WITHDRAWAL_QUEUE = lidoWithdrawalQueue; - _EIGEN_STETH_STRATEGY = stETHStrategy; - _EIGEN_STRATEGY_MANAGER = eigenStrategyManager; _disableInitializers(); } @@ -89,7 +67,7 @@ contract PufferVault is // If we don't use this pattern, somebody can create a Lido withdrawal, claim it to this contract // Making `$.lidoLockedETH -= msg.value` revert VaultStorage storage $ = _getPufferVaultStorage(); - if ($.isLidoWithdrawal) { + if ($.deprecated_isLidoWithdrawal) { $.lidoLockedETH -= msg.value; } } @@ -118,7 +96,7 @@ contract PufferVault is VaultStorage storage $ = _getPufferVaultStorage(); // Tell our receive() that we are doing a Lido claim - $.isLidoWithdrawal = true; + $.deprecated_isLidoWithdrawal = true; for (uint256 i = 0; i < requestIds.length; ++i) { bool isValidWithdrawal = $.lidoWithdrawals.remove(requestIds[i]); @@ -131,24 +109,10 @@ contract PufferVault is } // Reset back the value - $.isLidoWithdrawal = false; + $.deprecated_isLidoWithdrawal = false; emit ClaimedWithdrawals(requestIds); } - /** - * @notice Not allowed - */ - function redeem(uint256, address, address) public virtual override returns (uint256) { - revert WithdrawalsAreDisabled(); - } - - /** - * @notice Not allowed - */ - function withdraw(uint256, address, address) public virtual override returns (uint256) { - revert WithdrawalsAreDisabled(); - } - /** * @dev See {IERC4626-totalAssets}. * Eventually, stETH will not be part of this vault anymore, and the Vault(pufETH) will represent shares of total ETH holdings @@ -158,21 +122,7 @@ contract PufferVault is * + ETH balance of this vault */ function totalAssets() public view virtual override returns (uint256) { - return _ST_ETH.balanceOf(address(this)) + getELBackingEthAmount() + getPendingLidoETHAmount() - + address(this).balance; - } - - /** - * @notice Returns the ETH amount that is backing this vault locked in EigenLayer stETH strategy - */ - function getELBackingEthAmount() public view virtual returns (uint256 ethAmount) { - VaultStorage storage $ = _getPufferVaultStorage(); - // When we initiate withdrawal from EigenLayer, the shares are deducted from the `lockedAmount` - // In that case the locked amount goes to 0 and the pendingWithdrawalAmount increases - uint256 lockedAmount = _EIGEN_STETH_STRATEGY.userUnderlyingView(address(this)); - uint256 pendingWithdrawalAmount = - _EIGEN_STETH_STRATEGY.sharesToUnderlyingView($.eigenLayerPendingWithdrawalSharesAmount); - return lockedAmount + pendingWithdrawalAmount; + return _ST_ETH.balanceOf(address(this)) + getPendingLidoETHAmount() + address(this).balance; } /** @@ -184,75 +134,6 @@ contract PufferVault is return $.lidoLockedETH; } - /** - * @notice Deposits stETH into `stETH EigenLayer strategy` - * Restricted access - * @param amount the amount of stETH to deposit - */ - function depositToEigenLayer(uint256 amount) external virtual restricted { - SafeERC20.safeIncreaseAllowance(_ST_ETH, address(_EIGEN_STRATEGY_MANAGER), amount); - _EIGEN_STRATEGY_MANAGER.depositIntoStrategy({ strategy: _EIGEN_STETH_STRATEGY, token: _ST_ETH, amount: amount }); - } - - /** - * @notice Initiates stETH withdrawals from EigenLayer - * Restricted access - * @param sharesToWithdraw An amount of EigenLayer shares that we want to queue - */ - function initiateStETHWithdrawalFromEigenLayer(uint256 sharesToWithdraw) external virtual restricted { - VaultStorage storage $ = _getPufferVaultStorage(); - - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = IStrategy(_EIGEN_STETH_STRATEGY); - - uint256[] memory shares = new uint256[](1); - shares[0] = sharesToWithdraw; - - // Account for the shares - $.eigenLayerPendingWithdrawalSharesAmount += sharesToWithdraw; - - bytes32 withdrawalRoot = _EIGEN_STRATEGY_MANAGER.queueWithdrawal({ - strategyIndexes: new uint256[](1), // [0] - strategies: strategies, - shares: shares, - withdrawer: address(this), - undelegateIfPossible: true - }); - - $.eigenLayerWithdrawals.add(withdrawalRoot); - } - - /** - * @notice Claims stETH withdrawals from EigenLayer - * Restricted access - * @param queuedWithdrawal The queued withdrawal details - * @param tokens The tokens to be withdrawn - * @param middlewareTimesIndex The index of middleware times - */ - function claimWithdrawalFromEigenLayer( - IEigenLayer.QueuedWithdrawal calldata queuedWithdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex - ) external virtual { - VaultStorage storage $ = _getPufferVaultStorage(); - - bytes32 withdrawalRoot = _EIGEN_STRATEGY_MANAGER.calculateWithdrawalRoot(queuedWithdrawal); - bool isValidWithdrawal = $.eigenLayerWithdrawals.remove(withdrawalRoot); - if (!isValidWithdrawal) { - revert InvalidWithdrawal(); - } - - // nosemgrep basic-arithmetic-underflow - $.eigenLayerPendingWithdrawalSharesAmount -= queuedWithdrawal.shares[0]; - - _EIGEN_STRATEGY_MANAGER.completeQueuedWithdrawal({ - queuedWithdrawal: queuedWithdrawal, - tokens: tokens, - middlewareTimesIndex: middlewareTimesIndex, - receiveAsTokens: true - }); - } - /** * @notice Initiates ETH withdrawals from Lido * Restricted access diff --git a/mainnet-contracts/src/PufferVaultStorage.sol b/mainnet-contracts/src/PufferVaultStorage.sol index 44db204c..f64829fc 100644 --- a/mainnet-contracts/src/PufferVaultStorage.sol +++ b/mainnet-contracts/src/PufferVaultStorage.sol @@ -21,10 +21,10 @@ abstract contract PufferVaultStorage { struct VaultStorage { // 6 Slots for Redemption logic uint256 lidoLockedETH; - uint256 eigenLayerPendingWithdrawalSharesAmount; - bool isLidoWithdrawal; // Not in use in PufferVaultV2 + uint256 deprecated_eigenLayerPendingWithdrawalSharesAmount; // Not in use anymore + bool deprecated_isLidoWithdrawal; // Not in use in PufferVaultV2 EnumerableSet.UintSet lidoWithdrawals; // Not in use in PufferVaultV2 - EnumerableSet.Bytes32Set eigenLayerWithdrawals; + EnumerableSet.Bytes32Set deprecated_eigenLayerWithdrawals; // Not in use anymore EnumerableMap.UintToUintMap lidoWithdrawalAmounts; // 1 Slot for daily withdrawal limits uint96 dailyAssetsWithdrawalLimit; diff --git a/mainnet-contracts/src/PufferVaultV2.sol b/mainnet-contracts/src/PufferVaultV2.sol index ad885b0b..fb82f3a0 100644 --- a/mainnet-contracts/src/PufferVaultV2.sol +++ b/mainnet-contracts/src/PufferVaultV2.sol @@ -4,16 +4,12 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVault } from "./PufferVault.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV2 } from "./interface/IPufferVaultV2.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; /** @@ -41,12 +37,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { */ IPufferOracle public immutable PUFFER_ORACLE; - /** - * @notice Delegation manager from EigenLayer - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ - IDelegationManager internal immutable _DELEGATION_MANAGER; - /** * @dev Two wallets that transferred pufETH to the PufferVault by mistake. * @custom:oz-upgrades-unsafe-allow state-variable-immutable @@ -62,18 +52,11 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { /** * @custom:oz-upgrades-unsafe-allow constructor */ - constructor( - IStETH stETH, - IWETH weth, - ILidoWithdrawalQueue lidoWithdrawalQueue, - IStrategy stETHStrategy, - IEigenLayer eigenStrategyManager, - IPufferOracle oracle, - IDelegationManager delegationManager - ) PufferVault(stETH, lidoWithdrawalQueue, stETHStrategy, eigenStrategyManager) { + constructor(IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, IPufferOracle oracle) + PufferVault(stETH, lidoWithdrawalQueue) + { _WETH = weth; PUFFER_ORACLE = oracle; - _DELEGATION_MANAGER = delegationManager; ERC4626Storage storage erc4626Storage = _getERC4626StorageInternal(); erc4626Storage._asset = _WETH; // This redundant code is for the Echidna fuzz testing @@ -134,8 +117,8 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { assembly { callValue := callvalue() } - return _ST_ETH.balanceOf(address(this)) + getPendingLidoETHAmount() + getELBackingEthAmount() - + _WETH.balanceOf(address(this)) + (address(this).balance - callValue) + PUFFER_ORACLE.getLockedEthAmount(); + return _ST_ETH.balanceOf(address(this)) + getPendingLidoETHAmount() + _WETH.balanceOf(address(this)) + + (address(this).balance - callValue) + PUFFER_ORACLE.getLockedEthAmount(); } /** @@ -472,16 +455,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { return $.exitFeeBasisPoints; } - // Not compatible anymore - function claimWithdrawalFromEigenLayer( - IEigenLayer.QueuedWithdrawal calldata queuedWithdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex - ) external override { } - - // Not needed anymore - function depositToEigenLayer(uint256 amount) external override { } - /** * @dev Calculates the fees that should be added to an amount `assets` that does not already include fees. * Used in {IERC4626-withdraw}. diff --git a/mainnet-contracts/src/PufferVaultV3.sol b/mainnet-contracts/src/PufferVaultV3.sol index db0a2fc4..895e9e80 100644 --- a/mainnet-contracts/src/PufferVaultV3.sol +++ b/mainnet-contracts/src/PufferVaultV3.sol @@ -4,9 +4,6 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "./PufferVaultV2.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; @@ -26,21 +23,12 @@ contract PufferVaultV3 is PufferVaultV2, IPufferVaultV3 { * @param stETH Address of the stETH token contract. * @param weth Address of the WETH token contract. * @param lidoWithdrawalQueue Address of the Lido withdrawal queue contract. - * @param stETHStrategy Address of the stETH strategy contract. - * @param eigenStrategyManager Address of the EigenLayer strategy manager contract. * @param oracle Address of the PufferOracle contract. - * @param delegationManager Address of the delegation manager contract. * @custom:oz-upgrades-unsafe-allow constructor */ - constructor( - IStETH stETH, - IWETH weth, - ILidoWithdrawalQueue lidoWithdrawalQueue, - IStrategy stETHStrategy, - IEigenLayer eigenStrategyManager, - IPufferOracle oracle, - IDelegationManager delegationManager - ) PufferVaultV2(stETH, weth, lidoWithdrawalQueue, stETHStrategy, eigenStrategyManager, oracle, delegationManager) { + constructor(IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, IPufferOracle oracle) + PufferVaultV2(stETH, weth, lidoWithdrawalQueue, oracle) + { _disableInitializers(); } diff --git a/mainnet-contracts/src/PufferVaultV4.sol b/mainnet-contracts/src/PufferVaultV4.sol index 474f1f0e..f2903e13 100644 --- a/mainnet-contracts/src/PufferVaultV4.sol +++ b/mainnet-contracts/src/PufferVaultV4.sol @@ -3,9 +3,6 @@ pragma solidity >=0.8.0 <0.9.0; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "./interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "./interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "./interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; import { PufferVaultV3 } from "./PufferVaultV3.sol"; @@ -32,22 +29,16 @@ contract PufferVaultV4 is PufferVaultV3 { * @param stETH Address of the stETH token contract. * @param weth Address of the WETH token contract. * @param lidoWithdrawalQueue Address of the Lido withdrawal queue contract. - * @param stETHStrategy Address of the stETH strategy contract. - * @param eigenStrategyManager Address of the EigenLayer strategy manager contract. * @param oracle Address of the PufferOracle contract. - * @param delegationManager Address of the delegation manager contract. * @custom:oz-upgrades-unsafe-allow constructor */ constructor( IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, - IStrategy stETHStrategy, - IEigenLayer eigenStrategyManager, IPufferOracle oracle, - IDelegationManager delegationManager, IPufferRevenueDepositor revenueDepositor - ) PufferVaultV3(stETH, weth, lidoWithdrawalQueue, stETHStrategy, eigenStrategyManager, oracle, delegationManager) { + ) PufferVaultV3(stETH, weth, lidoWithdrawalQueue, oracle) { RESTAKING_REWARDS_DEPOSITOR = revenueDepositor; _disableInitializers(); } diff --git a/mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol b/mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol index 8e60b9b7..f4244f11 100644 --- a/mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol +++ b/mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol @@ -5,22 +5,15 @@ import { PufferVaultV2 } from "../PufferVaultV2.sol"; import { WETH9 } from "../../test/mocks/WETH9.sol"; import { stETHMock } from "../../test/mocks/stETHMock.sol"; import { MockPufferOracle } from "../../test/mocks/MockPufferOracle.sol"; -import { EigenLayerManagerMock } from "../../test/mocks/EigenLayerManagerMock.sol"; -import { EigenLayerDelegationManagerMock } from "../../test/mocks/EigenLayerDelegationManagerMock.sol"; import { LidoWithdrawalQueueMock } from "../../test/mocks/LidoWithdrawalQueueMock.sol"; -import { stETHStrategyMock } from "../../test/mocks/stETHStrategyMock.sol"; -import { TestERC20Token } from "@crytic/contracts/ERC4626/util/TestERC20Token.sol"; contract EchidnaPufferVaultV2 is CryticERC4626PropertyTests { constructor() { WETH9 weth = new WETH9(); stETHMock stETH = new stETHMock(); MockPufferOracle oracle = new MockPufferOracle(); - EigenLayerManagerMock eigenlayer = new EigenLayerManagerMock(); LidoWithdrawalQueueMock lido = new LidoWithdrawalQueueMock(); - stETHStrategyMock stETHStrategy = new stETHStrategyMock(); - EigenLayerDelegationManagerMock delegationMock = new EigenLayerDelegationManagerMock(); - PufferVaultV2 vault = new PufferVaultV2(stETH, weth, lido, stETHStrategy, eigenlayer, oracle, delegationMock); + PufferVaultV2 vault = new PufferVaultV2(stETH, weth, lido, oracle); initialize(address(vault), address(weth), false); } } diff --git a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol index f88ba029..8c1ad70f 100644 --- a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol @@ -49,10 +49,7 @@ contract PufferRevenueDepositorForkTest is MainnetForkTestHelper { IStETH(_getStETH()), IWETH(_getWETH()), ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - IStrategy(_getStETHStrategy()), - IEigenLayer(_getEigenLayerStrategyManager()), IPufferOracle(_getPufferOracle()), - IDelegationManager(_getDelegationManager()), revenueDepositor ) ); diff --git a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol index a0d26c7c..49f26eaf 100644 --- a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol +++ b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol @@ -177,15 +177,7 @@ contract PufferTest is Test { // Simulate that our deployed oracle becomes active and starts posting results of Puffer staking // At this time, we stop accepting stETH, and we accept only native ETH - PufferVaultV2 newImplementation = new PufferVaultV2Tests( - _ST_ETH, - _WETH, - _LIDO_WITHDRAWAL_QUEUE, - _EIGEN_STETH_STRATEGY, - _EIGEN_STRATEGY_MANAGER, - mockOracle, - _EIGEN_DELEGATION_MANGER - ); + PufferVaultV2 newImplementation = new PufferVaultV2Tests(_ST_ETH, _WETH, _LIDO_WITHDRAWAL_QUEUE, mockOracle); // Community multisig can do thing instantly vm.startPrank(COMMUNITY_MULTISIG); @@ -451,162 +443,6 @@ contract PufferTest is Test { assertGt(pufferVault.balanceOf(alice), 0, "alice got pufETH"); } - function test_withdraw_from_eigenLayer_dos() - public - giveToken(BLAST_DEPOSIT, address(stETH), address(pufferVault), 1000 ether) // Blast got a lot of stETH - giveToken(BLAST_DEPOSIT, address(stETH), alice, 1 ether) // Blast got a lot of stETH - { - // Simulate stETH cap increase call on EL - _increaseELstETHCap(); - - // Deposit to EL - vm.startPrank(OPERATIONS_MULTISIG); - pufferVault.depositToEigenLayer(stETH.balanceOf(address(pufferVault))); - - uint256 ownedShares = _EIGEN_STRATEGY_MANAGER.stakerStrategyShares(address(pufferVault), _EIGEN_STETH_STRATEGY); - - uint256 assetsBefore = pufferVault.totalAssets(); - - // Initiate the withdrawal for PufferVault - pufferVault.initiateStETHWithdrawalFromEigenLayer(ownedShares); - - // Alice deposits to EL - vm.startPrank(alice); - SafeERC20.safeIncreaseAllowance(_ST_ETH, address(_EIGEN_STRATEGY_MANAGER), 1 ether); - _EIGEN_STRATEGY_MANAGER.depositIntoStrategy({ strategy: _EIGEN_STETH_STRATEGY, token: _ST_ETH, amount: 1 ether }); - - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = IStrategy(_EIGEN_STETH_STRATEGY); - - IERC20[] memory tokens = new IERC20[](1); - tokens[0] = IERC20(address(stETH)); - - uint256[] memory aliceShares = new uint256[](1); - aliceShares[0] = _EIGEN_STRATEGY_MANAGER.stakerStrategyShares(address(alice), _EIGEN_STETH_STRATEGY); - - IEigenLayer.WithdrawerAndNonce memory withdrawerAndNonce = - IEigenLayer.WithdrawerAndNonce({ withdrawer: address(pufferVault), nonce: 0 }); - - IEigenLayer.QueuedWithdrawal memory aliceQueuedWithdrawal = IEigenLayer.QueuedWithdrawal({ - strategies: strategies, - shares: aliceShares, - depositor: address(alice), - withdrawerAndNonce: withdrawerAndNonce, - withdrawalStartBlock: uint32(block.number), - delegatedAddress: address(0) - }); - - // Queue withdrawal form alice - _EIGEN_STRATEGY_MANAGER.queueWithdrawal({ - strategyIndexes: new uint256[](1), // [0] - strategies: strategies, - shares: aliceShares, - withdrawer: address(pufferVault), - undelegateIfPossible: true - }); - - // PufferVault withdrawal - uint256[] memory shares = new uint256[](1); - shares[0] = ownedShares; - - IEigenLayer.WithdrawerAndNonce memory withdrawerAndNonceFromTheVault = - IEigenLayer.WithdrawerAndNonce({ withdrawer: address(pufferVault), nonce: 0 }); - - IEigenLayer.QueuedWithdrawal memory queuedWithdrawal = IEigenLayer.QueuedWithdrawal({ - strategies: strategies, - shares: shares, - depositor: address(pufferVault), - withdrawerAndNonce: withdrawerAndNonceFromTheVault, - withdrawalStartBlock: uint32(block.number), - delegatedAddress: address(0) - }); - - // Roll block number + 100k blocks into the future - vm.roll(block.number + 100000); - - // Alice should not be able to withdraw through PufferVault - vm.expectRevert(IPufferVault.InvalidWithdrawal.selector); - pufferVault.claimWithdrawalFromEigenLayer(aliceQueuedWithdrawal, tokens, 0); - - // 1 wei diff because of rounding - assertApproxEqAbs(assetsBefore, pufferVault.totalAssets(), 1, "should remain the same when locked"); - - // Normal PufferVault Withdrawal should work - pufferVault.claimWithdrawalFromEigenLayer(queuedWithdrawal, tokens, 0); - - // 1 wei diff because of rounding - assertApproxEqAbs(assetsBefore, pufferVault.totalAssets(), 1, "should remain the same after withdrawal"); - } - - function test_withdraw_from_eigenLayer() - public - giveToken(BLAST_DEPOSIT, address(stETH), address(pufferVault), 1000 ether) // Blast got a lot of stETH - { - // Simulate stETH cap increase call on EL - _increaseELstETHCap(); - - vm.startPrank(OPERATIONS_MULTISIG); - pufferVault.depositToEigenLayer(stETH.balanceOf(address(pufferVault))); - - uint256 ownedShares = _EIGEN_STRATEGY_MANAGER.stakerStrategyShares(address(pufferVault), _EIGEN_STETH_STRATEGY); - - uint256 assetsBefore = pufferVault.totalAssets(); - - // Initiate the withdrawal - pufferVault.initiateStETHWithdrawalFromEigenLayer(ownedShares); - - // 1 wei diff because of rounding - assertApproxEqAbs(assetsBefore, pufferVault.totalAssets(), 1, "should remain the same when locked"); - - IERC20[] memory tokens = new IERC20[](1); - tokens[0] = IERC20(address(stETH)); - - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = IStrategy(_EIGEN_STETH_STRATEGY); - - uint256[] memory shares = new uint256[](1); - shares[0] = ownedShares; - - IEigenLayer.WithdrawerAndNonce memory withdrawerAndNonce = - IEigenLayer.WithdrawerAndNonce({ withdrawer: address(pufferVault), nonce: 0 }); - - IEigenLayer.QueuedWithdrawal memory queuedWithdrawal = IEigenLayer.QueuedWithdrawal({ - strategies: strategies, - shares: shares, - depositor: address(pufferVault), - withdrawerAndNonce: withdrawerAndNonce, - withdrawalStartBlock: uint32(block.number), - delegatedAddress: address(0) - }); - - // Roll block number + 100k blocks into the future - vm.roll(block.number + 100000); - - // Claim Withdrawal - pufferVault.claimWithdrawalFromEigenLayer(queuedWithdrawal, tokens, 0); - - // 1 wei diff because of rounding - assertApproxEqAbs(assetsBefore, pufferVault.totalAssets(), 1, "should remain the same after withdrawal"); - } - - function test_eigenlayer_cap_reached() - public - giveToken(BLAST_DEPOSIT, address(stETH), address(pufferVault), 1000 ether) // Blast got a lot of stETH - { - uint256 assetsBefore = pufferVault.totalAssets(); - - // 1 wei diff because of rounding - assertApproxEqAbs(assetsBefore, 1000 ether, 1, "should have 1k ether"); - - vm.startPrank(OPERATIONS_MULTISIG); - // EL Reverts - vm.expectRevert("Pausable: index is paused"); - pufferVault.depositToEigenLayer(1000 ether); - - // 1 wei diff because of rounding - assertApproxEqAbs(pufferVault.totalAssets(), 1000 ether, 1, "should have 1k ether after"); - } - function _signPermit(_TestTemps memory t) internal pure returns (Permit memory p) { bytes32 innerHash = keccak256(abi.encode(_PERMIT_TYPEHASH, t.owner, t.to, t.amount, t.nonce, t.deadline)); bytes32 domainSeparator = t.domainSeparator; diff --git a/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol index 4d945312..fc579134 100644 --- a/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol @@ -51,7 +51,6 @@ contract PufferVaultV2ForkTest is MainnetForkTestHelper { assertEq(pufferVault.getPendingLidoETHAmount(), 0, "0 pending lido eth"); assertEq(pufferVault.totalAssets(), 368072.286049064583783628 ether, "total assets"); assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 100 ether, "daily withdrawal limit"); - assertEq(pufferVault.getELBackingEthAmount(), 342289.36625576203463247 ether, "0 EL backing eth"); // mainnet fork 19431593); assertEq(pufferVault.getExitFeeBasisPoints(), 100, "1% withdrawal fee"); } diff --git a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol index ef60f431..c77aba46 100644 --- a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol @@ -49,20 +49,9 @@ contract PufferVaultWithdrawalTest is Test { stETH = IStETH(address(0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034)); IWETH weth = IWETH(0xD6eF375Ad62f1d5BC06479fD0c7DCEF28e5Dc898); ILidoWithdrawalQueue lidoWithdrawalQueue = ILidoWithdrawalQueue(0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50); - IStrategy stETHStrategy = IStrategy(0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3); - IEigenLayer eigenStrategyManager = IEigenLayer(0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6); MockPufferOracle oracle = new MockPufferOracle(); - IDelegationManager delegationManager = IDelegationManager(0xA44151489861Fe9e3055d95adC98FbD462B948e7); - newImpl = new PufferVaultV2( - stETH, - weth, - lidoWithdrawalQueue, - stETHStrategy, - eigenStrategyManager, - IPufferOracle(address(oracle)), - delegationManager - ); + newImpl = new PufferVaultV2(stETH, weth, lidoWithdrawalQueue, IPufferOracle(address(oracle))); } // Update contracts and setup access diff --git a/mainnet-contracts/test/MainnetForkTestHelper.sol b/mainnet-contracts/test/MainnetForkTestHelper.sol index 5ea810d6..ab7cbe97 100644 --- a/mainnet-contracts/test/MainnetForkTestHelper.sol +++ b/mainnet-contracts/test/MainnetForkTestHelper.sol @@ -152,10 +152,7 @@ contract MainnetForkTestHelper is Test, DeployerHelper { stETH: IStETH(_getStETH()), weth: IWETH(_getWETH()), lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - stETHStrategy: IStrategy(_getStETHStrategy()), - eigenStrategyManager: IEigenLayer(_getEigenLayerStrategyManager()), - oracle: mockOracle, - delegationManager: IDelegationManager(_getEigenDelegationManager()) + oracle: mockOracle }); // Simulate that our deployed oracle becomes active and starts posting results of Puffer staking @@ -166,10 +163,7 @@ contract MainnetForkTestHelper is Test, DeployerHelper { stETH: IStETH(_getStETH()), weth: IWETH(_getWETH()), lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - stETHStrategy: IStrategy(_getStETHStrategy()), - eigenStrategyManager: IEigenLayer(_getEigenLayerStrategyManager()), - oracle: mockOracle, - delegationManager: IDelegationManager(_getEigenDelegationManager()) + oracle: mockOracle }); // Community multisig can do thing instantly @@ -228,10 +222,7 @@ contract MainnetForkTestHelper is Test, DeployerHelper { stETH: IStETH(_getStETH()), weth: IWETH(_getWETH()), lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - stETHStrategy: IStrategy(_getStETHStrategy()), - eigenStrategyManager: IEigenLayer(_getEigenLayerStrategyManager()), oracle: IPufferOracle(_getPufferOracle()), - delegationManager: IDelegationManager(_getEigenDelegationManager()), revenueDepositor: IPufferRevenueDepositor(address(0)) }); diff --git a/mainnet-contracts/test/mocks/DelegationManagerMock.sol b/mainnet-contracts/test/mocks/DelegationManagerMock.sol index 4f88b0b9..e186dcde 100644 --- a/mainnet-contracts/test/mocks/DelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/DelegationManagerMock.sol @@ -26,8 +26,6 @@ contract DelegationManagerMock { external { } - function updateOperatorMetadataURI(string calldata /*metadataURI*/ ) external pure { } - function delegateTo( address operator, IDelegationManager.SignatureWithExpiry memory, /*approverSignatureAndExpiry*/ @@ -178,20 +176,6 @@ contract DelegationManagerMock { return roots; } - function completeQueuedWithdrawal( - IDelegationManager.Withdrawal calldata withdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex, - bool receiveAsTokens - ) external { } - - function completeQueuedWithdrawals( - IDelegationManager.Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, - bool[] calldata receiveAsTokens - ) external { } - // onlyDelegationManager functions in StrategyManager function addShares( IStrategyManager strategyManager, diff --git a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol index a23c9aff..fac2c48b 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol @@ -4,22 +4,13 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV2 } from "src/PufferVaultV2.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; contract PufferVaultV2Tests is PufferVaultV2 { - constructor( - IStETH stETH, - IWETH weth, - ILidoWithdrawalQueue lidoWithdrawalQueue, - IStrategy stETHStrategy, - IEigenLayer eigenStrategyManager, - IPufferOracle oracle, - IDelegationManager delegationManager - ) PufferVaultV2(stETH, weth, lidoWithdrawalQueue, stETHStrategy, eigenStrategyManager, oracle, delegationManager) { + constructor(IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, IPufferOracle oracle) + PufferVaultV2(stETH, weth, lidoWithdrawalQueue, oracle) + { _WETH = weth; PUFFER_ORACLE = oracle; _disableInitializers(); diff --git a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol index e949f9e8..39f16238 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol @@ -4,11 +4,8 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultV4 } from "src/PufferVaultV4.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; import { IPufferOracle } from "src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IPufferRevenueDepositor } from "src/interface/IPufferRevenueDepositor.sol"; contract PufferVaultV4Tests is PufferVaultV4 { @@ -16,23 +13,9 @@ contract PufferVaultV4Tests is PufferVaultV4 { IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, - IStrategy stETHStrategy, - IEigenLayer eigenStrategyManager, IPufferOracle oracle, - IDelegationManager delegationManager, IPufferRevenueDepositor revenueDepositor - ) - PufferVaultV4( - stETH, - weth, - lidoWithdrawalQueue, - stETHStrategy, - eigenStrategyManager, - oracle, - delegationManager, - revenueDepositor - ) - { + ) PufferVaultV4(stETH, weth, lidoWithdrawalQueue, oracle, revenueDepositor) { _disableInitializers(); } diff --git a/mainnet-contracts/test/mocks/SlasherMock.sol b/mainnet-contracts/test/mocks/SlasherMock.sol deleted file mode 100644 index 09a2b9fd..00000000 --- a/mainnet-contracts/test/mocks/SlasherMock.sol +++ /dev/null @@ -1,615 +0,0 @@ -// // SPDX-License-Identifier: BUSL-1.1 -// pragma solidity >=0.8.12; - -// import "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; -// import "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -// import "src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; -// import "test/mocks/StructuredLinkedListMock.sol"; -// import "test/mocks/PausableMock.sol"; -// import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -// import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -// struct MiddlewareDetails { -// uint32 contractCanSlashOperatorUntilBlock; -// uint32 latestUpdateBlock; -// } - -// /** -// * @title The primary 'slashing' contract for EigenLayer. -// * @author Layr Labs, Inc. -// * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service -// * @notice This contract specifies details on slashing. The functionalities are: -// * - adding contracts who have permission to perform slashing, -// * - revoking permission for slashing from specified contracts, -// * - tracking historic stake updates to ensure that withdrawals can only be completed once no middlewares have slashing rights -// * over the funds being withdrawn -// */ -// contract SlasherMock is Initializable, OwnableUpgradeable, IAllocationManager { -// using StructuredLinkedListMock for StructuredLinkedListMock.List; - -// uint256 private constant HEAD = 0; - -// uint8 internal constant PAUSED_OPT_INTO_SLASHING = 0; -// uint8 internal constant PAUSED_FIRST_STAKE_UPDATE = 1; -// uint8 internal constant PAUSED_NEW_FREEZING = 2; - -// /// @notice The central StrategyManager contract of EigenLayer -// IStrategyManager public immutable strategyManager; -// /// @notice The DelegationManager contract of EigenLayer -// IDelegationManager public immutable delegation; -// // operator => whitelisted contract with slashing permissions => (the time before which the contract is allowed to slash the user, block it was last updated) -// mapping(address => mapping(address => MiddlewareDetails)) internal _whitelistedContractDetails; -// // staker => if their funds are 'frozen' and potentially subject to slashing or not -// mapping(address => bool) internal frozenStatus; - -// uint32 internal constant MAX_CAN_SLASH_UNTIL = type(uint32).max; - -// /** -// * operator => a linked list of the addresses of the whitelisted middleware with permission to slash the operator, i.e. which -// * the operator is serving. Sorted by the block at which they were last updated (content of updates below) in ascending order. -// * This means the 'HEAD' (i.e. start) of the linked list will have the stalest 'updateBlock' value. -// */ -// mapping(address => StructuredLinkedListMock.List) internal _operatorToWhitelistedContractsByUpdate; - -// /** -// * operator => -// * [ -// * ( -// * the least recent update block of all of the middlewares it's serving/served, -// * latest time that the stake bonded at that update needed to serve until -// * ) -// * ] -// */ -// mapping(address => MiddlewareDetails[]) internal _operatorToMiddlewareTimes; - -// constructor(IStrategyManager _strategyManager, IDelegationManager _delegation) { -// strategyManager = _strategyManager; -// delegation = _delegation; -// _disableInitializers(); -// } - -// /// @notice Ensures that the operator has opted into slashing by the caller, and that the caller has never revoked its slashing ability. -// modifier onlyRegisteredForService(address operator) { -// require( -// _whitelistedContractDetails[operator][msg.sender].contractCanSlashOperatorUntilBlock == MAX_CAN_SLASH_UNTIL, -// "Slasher.onlyRegisteredForService: Operator has not opted into slashing by caller" -// ); -// _; -// } - -// // EXTERNAL FUNCTIONS -// function initialize(address initialOwner, IPauserRegistry _pauserRegistry, uint256 initialPausedStatus) -// external -// initializer -// { -// _initializePauser(_pauserRegistry, initialPausedStatus); -// _transferOwnership(initialOwner); -// } - -// /** -// * @notice Gives the `contractAddress` permission to slash the funds of the caller. -// * @dev Typically, this function must be called prior to registering for a middleware. -// */ -// function optIntoSlashing(address contractAddress) external { -// //require(delegation.isOperator(msg.sender), "Slasher.optIntoSlashing: msg.sender is not a registered operator"); -// _optIntoSlashing(msg.sender, contractAddress); -// } - -// /** -// * @notice Used for 'slashing' a certain operator. -// * @param toBeFrozen The operator to be frozen. -// * @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop. -// * @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`. -// */ -// function freezeOperator(address toBeFrozen) external { -// require( -// canSlash(toBeFrozen, msg.sender), -// "Slasher.freezeOperator: msg.sender does not have permission to slash this operator" -// ); -// _freezeOperator(toBeFrozen, msg.sender); -// } - -// /** -// * @notice Removes the 'frozen' status from each of the `frozenAddresses` -// * @dev Callable only by the contract owner (i.e. governance). -// */ -// function resetFrozenStatus(address[] calldata frozenAddresses) external onlyOwner { -// for (uint256 i = 0; i < frozenAddresses.length;) { -// _resetFrozenStatus(frozenAddresses[i]); -// unchecked { -// ++i; -// } -// } -// } - -// /** -// * @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration -// * is slashable until serveUntilBlock -// * @param operator the operator whose stake update is being recorded -// * @param serveUntilBlock the block until which the operator's stake at the current block is slashable -// * @dev adds the middleware's slashing contract to the operator's linked list -// */ -// function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) -// external -// onlyRegisteredForService(operator) -// { -// // update the 'stalest' stakes update time + latest 'serveUntil' time of the `operator` -// _recordUpdateAndAddToMiddlewareTimes(operator, uint32(block.number), serveUntilBlock); - -// // Push the middleware to the end of the update list. This will fail if the caller *is* already in the list. -// require( -// _operatorToWhitelistedContractsByUpdate[operator].pushBack(_addressToUint(msg.sender)), -// "Slasher.recordFirstStakeUpdate: Appending middleware unsuccessful" -// ); -// } - -// /** -// * @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals) -// * to make sure the operator's stake at updateBlock is slashable until serveUntilBlock -// * @param operator the operator whose stake update is being recorded -// * @param updateBlock the block for which the stake update is being recorded -// * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable -// * @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after -// * @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions, -// * but it is anticipated to be rare and not detrimental. -// */ -// function recordStakeUpdate(address operator, uint32 updateBlock, uint32 serveUntilBlock, uint256 insertAfter) -// external -// onlyRegisteredForService(operator) -// { -// // sanity check on input -// require(updateBlock <= block.number, "Slasher.recordStakeUpdate: cannot provide update for future block"); -// // update the 'stalest' stakes update time + latest 'serveUntilBlock' of the `operator` -// _recordUpdateAndAddToMiddlewareTimes(operator, updateBlock, serveUntilBlock); - -// /** -// * Move the middleware to its correct update position, determined by `updateBlock` and indicated via `insertAfter`. -// * If the the middleware is the only one in the list, then no need to mutate the list -// */ -// if (_operatorToWhitelistedContractsByUpdate[operator].sizeOf() != 1) { -// // Remove the caller (middleware) from the list. This will fail if the caller is *not* already in the list. -// require( -// _operatorToWhitelistedContractsByUpdate[operator].remove(_addressToUint(msg.sender)) != 0, -// "Slasher.recordStakeUpdate: Removing middleware unsuccessful" -// ); -// // Run routine for updating the `operator`'s linked list of middlewares -// _updateMiddlewareList(operator, updateBlock, insertAfter); -// // if there is precisely one middleware in the list, then ensure that the caller is indeed the singular list entrant -// } else { -// require( -// _operatorToWhitelistedContractsByUpdate[operator].getHead() == _addressToUint(msg.sender), -// "Slasher.recordStakeUpdate: Caller is not the list entrant" -// ); -// } -// } - -// /** -// * @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration -// * is slashable until serveUntilBlock -// * @param operator the operator whose stake update is being recorded -// * @param serveUntilBlock the block until which the operator's stake at the current block is slashable -// * @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to -// * slash `operator` once `serveUntilBlock` is reached -// */ -// function recordLastStakeUpdateAndRevokeSlashingAbility(address operator, uint32 serveUntilBlock) -// external -// onlyRegisteredForService(operator) -// { -// // update the 'stalest' stakes update time + latest 'serveUntilBlock' of the `operator` -// _recordUpdateAndAddToMiddlewareTimes(operator, uint32(block.number), serveUntilBlock); -// // remove the middleware from the list -// require( -// _operatorToWhitelistedContractsByUpdate[operator].remove(_addressToUint(msg.sender)) != 0, -// "Slasher.recordLastStakeUpdateAndRevokeSlashingAbility: Removing middleware unsuccessful" -// ); -// // revoke the middleware's ability to slash `operator` after `serverUntil` -// _revokeSlashingAbility(operator, msg.sender, serveUntilBlock); -// } - -// // VIEW FUNCTIONS - -// /// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`. -// function contractCanSlashOperatorUntilBlock(address operator, address serviceContract) -// external -// view -// returns (uint32) -// { -// return _whitelistedContractDetails[operator][serviceContract].contractCanSlashOperatorUntilBlock; -// } - -// /// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake -// function latestUpdateBlock(address operator, address serviceContract) external view returns (uint32) { -// return _whitelistedContractDetails[operator][serviceContract].latestUpdateBlock; -// } - -// /* -// * @notice Returns `_whitelistedContractDetails[operator][serviceContract]`. -// * @dev A getter function like this appears to be necessary for returning a struct from storage in struct form, rather than as a tuple. -// */ -// function whitelistedContractDetails(address operator, address serviceContract) -// external -// view -// returns (MiddlewareDetails memory) -// { -// return _whitelistedContractDetails[operator][serviceContract]; -// } - -// /** -// * @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to -// * slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed -// * and the staker's status is reset (to 'unfrozen'). -// * @param staker The staker of interest. -// * @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated -// * to an operator who has their status set to frozen. Otherwise returns 'false'. -// */ -// function isFrozen(address staker) external view returns (bool) { -// if (frozenStatus[staker]) { -// return true; -// } else if (delegation.isDelegated(staker)) { -// address operatorAddress = delegation.delegatedTo(staker); -// return (frozenStatus[operatorAddress]); -// } else { -// return false; -// } -// } - -// /// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`. -// function canSlash(address toBeSlashed, address slashingContract) public view returns (bool) { -// if ( -// block.number < _whitelistedContractDetails[toBeSlashed][slashingContract].contractCanSlashOperatorUntilBlock -// ) { -// return true; -// } else { -// return false; -// } -// } - -// /** -// * @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used -// * to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `_operatorToMiddlewareTimes[operator]`). The specified -// * struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal. -// * This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event -// * that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist. -// * @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator, -// * this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`. -// * @param withdrawalStartBlock The block number at which the withdrawal was initiated. -// * @param middlewareTimesIndex Indicates an index in `_operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw -// * @dev The correct `middlewareTimesIndex` input should be computable off-chain. -// */ -// function canWithdraw(address operator, uint32 withdrawalStartBlock, uint256 middlewareTimesIndex) -// external -// view -// returns (bool) -// { -// // if the operator has never registered for a middleware, just return 'true' -// if (_operatorToMiddlewareTimes[operator].length == 0) { -// return true; -// } - -// // pull the MiddlewareTimes struct at the `middlewareTimesIndex`th position in `_operatorToMiddlewareTimes[operator]` -// MiddlewareTimes memory update = _operatorToMiddlewareTimes[operator][middlewareTimesIndex]; - -// /** -// * Case-handling for if the operator is not registered for any middlewares (i.e. they previously registered but are no longer registered for any), -// * AND the withdrawal was initiated after the 'stalestUpdateBlock' of the MiddlewareTimes struct specified by the provided `middlewareTimesIndex`. -// * NOTE: we check the 2nd of these 2 conditions first for gas efficiency, to help avoid an extra SLOAD in all other cases. -// */ -// if ( -// withdrawalStartBlock >= update.stalestUpdateBlock -// && _operatorToWhitelistedContractsByUpdate[operator].size == 0 -// ) { -// /** -// * In this case, we just check against the 'latestServeUntilBlock' of the last MiddlewareTimes struct. This is because the operator not being registered -// * for any middlewares (i.e. `_operatorToWhitelistedContractsByUpdate.size == 0`) means no new MiddlewareTimes structs will be being pushed, *and* the operator -// * will not be undertaking any new obligations (so just checking against the last entry is OK, unlike when the operator is actively registered for >=1 middleware). -// */ -// update = _operatorToMiddlewareTimes[operator][_operatorToMiddlewareTimes[operator].length - 1]; -// return (uint32(block.number) > update.latestServeUntilBlock); -// } - -// /** -// * Make sure the stalest update block at the time of the update is strictly after `withdrawalStartBlock` and ensure that the current time -// * is after the `latestServeUntilBlock` of the update. This assures us that this that all middlewares were updated after the withdrawal began, and -// * that the stake is no longer slashable. -// */ -// return (withdrawalStartBlock < update.stalestUpdateBlock && uint32(block.number) > update.latestServeUntilBlock); -// } - -// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][arrayIndex]`. -// function operatorToMiddlewareTimes(address operator, uint256 arrayIndex) -// external -// view -// returns (MiddlewareDetails memory) -// { -// return _operatorToMiddlewareTimes[operator][arrayIndex]; -// } - -// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator].length`. -// function middlewareTimesLength(address operator) external view returns (uint256) { -// return _operatorToMiddlewareTimes[operator].length; -// } - -// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`. -// function getMiddlewareTimesIndexStalestUpdateBlock(address operator, uint32 index) external view returns (uint32) { -// return _operatorToMiddlewareTimes[operator][index].stalestUpdateBlock; -// } - -// /// @notice Getter function for fetching `_operatorToMiddlewareTimes[operator][index].latestServeUntilBlock`. -// function getMiddlewareTimesIndexServeUntilBlock(address operator, uint32 index) external view returns (uint32) { -// return _operatorToMiddlewareTimes[operator][index].latestServeUntilBlock; -// } - -// /// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`. -// function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256) { -// return _operatorToWhitelistedContractsByUpdate[operator].size; -// } - -// /// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`). -// function operatorWhitelistedContractsLinkedListEntry(address operator, address node) -// external -// view -// returns (bool, uint256, uint256) -// { -// return StructuredLinkedListMock.getNode(_operatorToWhitelistedContractsByUpdate[operator], _addressToUint(node)); -// } - -// /** -// * @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`. -// * @dev Used within this contract only as a fallback in the case when an incorrect value of `insertAfter` is supplied as an input to `_updateMiddlewareList`. -// * @dev The return value should *either* be 'HEAD' (i.e. zero) in the event that the node being inserted in the linked list has an `updateBlock` -// * that is less than the HEAD of the list, *or* the return value should specify the last `node` in the linked list for which -// * `_whitelistedContractDetails[operator][node].latestUpdateBlock <= updateBlock`, -// * i.e. the node such that the *next* node either doesn't exist, -// * OR -// * `_whitelistedContractDetails[operator][nextNode].latestUpdateBlock > updateBlock`. -// */ -// function getCorrectValueForInsertAfter(address operator, uint32 updateBlock) public view returns (uint256) { -// uint256 node = _operatorToWhitelistedContractsByUpdate[operator].getHead(); -// /** -// * Special case: -// * If the node being inserted in the linked list has an `updateBlock` that is less than the HEAD of the list, then we set `insertAfter = HEAD`. -// * In _updateMiddlewareList(), the new node will be pushed to the front (HEAD) of the list. -// */ -// if (_whitelistedContractDetails[operator][_uintToAddress(node)].latestUpdateBlock > updateBlock) { -// return HEAD; -// } -// /** -// * `node` being zero (i.e. equal to 'HEAD') indicates an empty/non-existent node, i.e. reaching the end of the linked list. -// * Since the linked list is ordered in ascending order of update blocks, we simply start from the head of the list and step through until -// * we find a the *last* `node` for which `_whitelistedContractDetails[operator][node].latestUpdateBlock <= updateBlock`, or -// * otherwise reach the end of the list. -// */ -// (, uint256 nextNode) = _operatorToWhitelistedContractsByUpdate[operator].getNextNode(node); -// while ( -// (nextNode != HEAD) -// && (_whitelistedContractDetails[operator][_uintToAddress(node)].latestUpdateBlock <= updateBlock) -// ) { -// node = nextNode; -// (, nextNode) = _operatorToWhitelistedContractsByUpdate[operator].getNextNode(node); -// } -// return node; -// } - -// /// @notice gets the node previous to the given node in the operators middleware update linked list -// /// @dev used in offchain libs for updating stakes -// function getPreviousWhitelistedContractByUpdate(address operator, uint256 node) -// external -// view -// returns (bool, uint256) -// { -// return _operatorToWhitelistedContractsByUpdate[operator].getPreviousNode(node); -// } - -// // INTERNAL FUNCTIONS - -// function _optIntoSlashing(address operator, address contractAddress) internal { -// //allow the contract to slash anytime before a time VERY far in the future -// _whitelistedContractDetails[operator][contractAddress].contractCanSlashOperatorUntilBlock = MAX_CAN_SLASH_UNTIL; -// emit OptedIntoSlashing(operator, contractAddress); -// } - -// function _revokeSlashingAbility(address operator, address contractAddress, uint32 serveUntilBlock) internal { -// require( -// serveUntilBlock != MAX_CAN_SLASH_UNTIL, -// "Slasher._revokeSlashingAbility: serveUntilBlock time must be limited" -// ); -// // contractAddress can now only slash operator before `serveUntilBlock` -// _whitelistedContractDetails[operator][contractAddress].contractCanSlashOperatorUntilBlock = serveUntilBlock; -// emit SlashingAbilityRevoked(operator, contractAddress, serveUntilBlock); -// } - -// function _freezeOperator(address toBeFrozen, address slashingContract) internal { -// if (!frozenStatus[toBeFrozen]) { -// frozenStatus[toBeFrozen] = true; -// emit OperatorFrozen(toBeFrozen, slashingContract); -// } -// } - -// function _resetFrozenStatus(address previouslySlashedAddress) internal { -// if (frozenStatus[previouslySlashedAddress]) { -// frozenStatus[previouslySlashedAddress] = false; -// emit FrozenStatusReset(previouslySlashedAddress); -// } -// } - -// /** -// * @notice records the most recent updateBlock for the currently updating middleware and appends an entry to the operator's list of -// * MiddlewareTimes if relevant information has updated -// * @param operator the entity whose stake update is being recorded -// * @param updateBlock the block number for which the currently updating middleware is updating the serveUntilBlock for -// * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable -// * @dev this function is only called during externally called stake updates by middleware contracts that can slash operator -// */ -// function _recordUpdateAndAddToMiddlewareTimes(address operator, uint32 updateBlock, uint32 serveUntilBlock) -// internal -// { -// // reject any stale update, i.e. one from before that of the most recent recorded update for the currently updating middleware -// require( -// _whitelistedContractDetails[operator][msg.sender].latestUpdateBlock <= updateBlock, -// "Slasher._recordUpdateAndAddToMiddlewareTimes: can't push a previous update" -// ); -// _whitelistedContractDetails[operator][msg.sender].latestUpdateBlock = updateBlock; -// // get the latest recorded MiddlewareTimes, if the operator's list of MiddlwareTimes is non empty -// MiddlewareTimes memory curr; -// uint256 _operatorToMiddlewareTimesLength = _operatorToMiddlewareTimes[operator].length; -// if (_operatorToMiddlewareTimesLength != 0) { -// curr = _operatorToMiddlewareTimes[operator][_operatorToMiddlewareTimesLength - 1]; -// } -// MiddlewareTimes memory next = curr; -// bool pushToMiddlewareTimes; -// // if the serve until is later than the latest recorded one, update it -// if (serveUntilBlock > curr.latestServeUntilBlock) { -// next.latestServeUntilBlock = serveUntilBlock; -// // mark that we need push next to middleware times array because it contains new information -// pushToMiddlewareTimes = true; -// } - -// // If this is the very first middleware added to the operator's list of middleware, then we add an entry to _operatorToMiddlewareTimes -// if (_operatorToWhitelistedContractsByUpdate[operator].size == 0) { -// next.stalestUpdateBlock = updateBlock; -// pushToMiddlewareTimes = true; -// } -// // If the middleware is the first in the list, we will update the `stalestUpdateBlock` field in MiddlewareTimes -// else if (_operatorToWhitelistedContractsByUpdate[operator].getHead() == _addressToUint(msg.sender)) { -// // if the updated middleware was the earliest update, set it to the 2nd earliest update's update time -// (bool hasNext, uint256 nextNode) = -// _operatorToWhitelistedContractsByUpdate[operator].getNextNode(_addressToUint(msg.sender)); - -// if (hasNext) { -// // get the next middleware's latest update block -// uint32 nextMiddlewaresLeastRecentUpdateBlock = -// _whitelistedContractDetails[operator][_uintToAddress(nextNode)].latestUpdateBlock; -// if (nextMiddlewaresLeastRecentUpdateBlock < updateBlock) { -// // if there is a next node, then set the stalestUpdateBlock to its recorded value -// next.stalestUpdateBlock = nextMiddlewaresLeastRecentUpdateBlock; -// } else { -// //otherwise updateBlock is the least recent update as well -// next.stalestUpdateBlock = updateBlock; -// } -// } else { -// // otherwise this is the only middleware so right now is the stalestUpdateBlock -// next.stalestUpdateBlock = updateBlock; -// } -// // mark that we need to push `next` to middleware times array because it contains new information -// pushToMiddlewareTimes = true; -// } - -// // if `next` has new information, then push it -// if (pushToMiddlewareTimes) { -// _operatorToMiddlewareTimes[operator].push(next); -// emit MiddlewareTimesAdded( -// operator, -// _operatorToMiddlewareTimes[operator].length - 1, -// next.stalestUpdateBlock, -// next.latestServeUntilBlock -// ); -// } -// } - -// /// @notice A routine for updating the `operator`'s linked list of middlewares, inside `recordStakeUpdate`. -// function _updateMiddlewareList(address operator, uint32 updateBlock, uint256 insertAfter) internal { -// /** -// * boolean used to track if the `insertAfter input to this function is incorrect. If it is, then `runFallbackRoutine` will -// * be flipped to 'true', and we will use `getCorrectValueForInsertAfter` to find the correct input. This routine helps solve -// * a race condition where the proper value of `insertAfter` changes while a transaction is pending. -// */ -// bool runFallbackRoutine = false; -// // If this condition is met, then the `updateBlock` input should be after `insertAfter`'s latest updateBlock -// if (insertAfter != HEAD) { -// // Check that `insertAfter` exists. If not, we will use the fallback routine to find the correct value for `insertAfter`. -// if (!_operatorToWhitelistedContractsByUpdate[operator].nodeExists(insertAfter)) { -// runFallbackRoutine = true; -// } - -// /** -// * Make sure `insertAfter` specifies a node for which the most recent updateBlock was *at or before* updateBlock. -// * Again, if not, we will use the fallback routine to find the correct value for `insertAfter`. -// */ -// if ( -// (!runFallbackRoutine) -// && (_whitelistedContractDetails[operator][_uintToAddress(insertAfter)].latestUpdateBlock > updateBlock) -// ) { -// runFallbackRoutine = true; -// } - -// // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct so far -// if (!runFallbackRoutine) { -// // Get `insertAfter`'s successor. `hasNext` will be false if `insertAfter` is the last node in the list -// (bool hasNext, uint256 nextNode) = -// _operatorToWhitelistedContractsByUpdate[operator].getNextNode(insertAfter); -// if (hasNext) { -// /** -// * Make sure the element after `insertAfter`'s most recent updateBlock was *strictly after* `updateBlock`. -// * Again, if not, we will use the fallback routine to find the correct value for `insertAfter`. -// */ -// if ( -// _whitelistedContractDetails[operator][_uintToAddress(nextNode)].latestUpdateBlock <= updateBlock -// ) { -// runFallbackRoutine = true; -// } -// } -// } - -// // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct on all counts -// if (!runFallbackRoutine) { -// /** -// * Insert the caller (middleware) after `insertAfter`. -// * This will fail if `msg.sender` is already in the list, which they shouldn't be because they were removed from the list above. -// */ -// require( -// _operatorToWhitelistedContractsByUpdate[operator].insertAfter( -// insertAfter, _addressToUint(msg.sender) -// ), -// "Slasher.recordStakeUpdate: Inserting middleware unsuccessful" -// ); -// // in this case (runFallbackRoutine == true), we run a search routine to find the correct input value of `insertAfter` and then rerun this function -// } else { -// insertAfter = getCorrectValueForInsertAfter(operator, updateBlock); -// _updateMiddlewareList(operator, updateBlock, insertAfter); -// } -// // In this case (insertAfter == HEAD), the `updateBlock` input should be before every other middleware's latest updateBlock. -// } else { -// /** -// * Check that `updateBlock` is before any other middleware's latest updateBlock. -// * If not, use the fallback routine to find the correct value for `insertAfter`. -// */ -// if ( -// _whitelistedContractDetails[operator][_uintToAddress( -// _operatorToWhitelistedContractsByUpdate[operator].getHead() -// )].latestUpdateBlock <= updateBlock -// ) { -// runFallbackRoutine = true; -// } -// // if we have not marked `runFallbackRoutine` as 'true' yet, then that means the `insertAfter` input was correct on all counts -// if (!runFallbackRoutine) { -// /** -// * Insert the middleware at the start (i.e. HEAD) of the list. -// * This will fail if `msg.sender` is already in the list, which they shouldn't be because they were removed from the list above. -// */ -// require( -// _operatorToWhitelistedContractsByUpdate[operator].pushFront(_addressToUint(msg.sender)), -// "Slasher.recordStakeUpdate: Preppending middleware unsuccessful" -// ); -// // in this case (runFallbackRoutine == true), we run a search routine to find the correct input value of `insertAfter` and then rerun this function -// } else { -// insertAfter = getCorrectValueForInsertAfter(operator, updateBlock); -// _updateMiddlewareList(operator, updateBlock, insertAfter); -// } -// } -// } - -// function _addressToUint(address addr) internal pure returns (uint256) { -// return uint256(uint160(addr)); -// } - -// function _uintToAddress(uint256 x) internal pure returns (address) { -// return address(uint160(x)); -// } - -// /** -// * @dev This empty reserved space is put in place to allow future versions to add new -// * variables without shifting down storage in the inheritance chain. -// * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps -// */ -// uint256[46] private __gap; -// } diff --git a/mainnet-contracts/test/unit/PufETH.t.sol b/mainnet-contracts/test/unit/PufETH.t.sol index 79a3193d..cdc49510 100644 --- a/mainnet-contracts/test/unit/PufETH.t.sol +++ b/mainnet-contracts/test/unit/PufETH.t.sol @@ -56,13 +56,6 @@ contract PufETHTest is ERC4626Test { assertEq(pufferVault.previewWithdraw(1000 ether), 1000 ether, "preview withdraw"); assertEq(pufferVault.maxRedeem(address(this)), 2000 ether, "maxRedeem"); assertEq(pufferVault.previewRedeem(1000 ether), 1000 ether, "previewRedeem"); - - // Withdrawals are disabled - vm.expectRevert(IPufferVault.WithdrawalsAreDisabled.selector); - pufferVault.withdraw(1000 ether, address(this), address(this)); - - vm.expectRevert(IPufferVault.WithdrawalsAreDisabled.selector); - pufferVault.redeem(1000 ether, address(this), address(this)); } function test_roles_setup() public { @@ -73,12 +66,6 @@ contract PufETHTest is ERC4626Test { vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, msgSender)); pufferVault.initiateETHWithdrawalsFromLido(amounts); - vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, msgSender)); - pufferVault.depositToEigenLayer(1); - - vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, msgSender)); - pufferVault.initiateStETHWithdrawalFromEigenLayer(1); - vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, msgSender)); pufferVault.upgradeToAndCall(address(pufferDepositor), ""); } diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index 4fe6a6e4..e0b43565 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.0 <0.9.0; import { UnitTestHelper } from "../helpers/UnitTestHelper.sol"; import { PufferModule } from "../../src/PufferModule.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; -import { AVSContractsRegistry } from "../../src/AVSContractsRegistry.sol"; import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; @@ -14,8 +13,10 @@ import { Unauthorized } from "../../src/Errors.sol"; import { ROLE_ID_OPERATIONS_PAYMASTER } from "../../script/Roles.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IDelegationManagerTypes } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { RestakingOperator } from "src/RestakingOperator.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; +import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; contract PufferModuleUpgrade { function getMagicValue() external pure returns (uint256) { @@ -24,6 +25,8 @@ contract PufferModuleUpgrade { } contract PufferModuleManagerTest is UnitTestHelper { + address public BEACON_CHAIN_STRATEGY = 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0; + Merkle rewardsMerkleProof; bytes32[] rewardsMerkleProofData; @@ -225,11 +228,39 @@ contract PufferModuleManagerTest is UnitTestHelper { pufferModuleManager.callSetClaimerFor(address(operator), claimer); } - function test_completeQueuedWithdrawals(bytes32 moduleName) public { + function test_completeQueuedWithdrawalsEmpty(bytes32 moduleName) public { vm.assume(pufferProtocol.getModuleAddress(moduleName) == address(0)); _createPufferModule(moduleName); - IDelegationManager.Withdrawal[] memory withdrawals; + IDelegationManagerTypes.Withdrawal[] memory withdrawals; + IERC20[][] memory tokens; + bool[] memory receiveAsTokens; + + emit IPufferModuleManager.CompletedQueuedWithdrawals(moduleName, 0); + pufferModuleManager.callCompleteQueuedWithdrawals(moduleName, withdrawals, tokens, receiveAsTokens); + } + + function test_completeQueuedWithdrawalsFull(bytes32 moduleName) public { + vm.assume(pufferProtocol.getModuleAddress(moduleName) == address(0)); + _createPufferModule(moduleName); + + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = IStrategy(BEACON_CHAIN_STRATEGY); + + uint256[] memory scaledShares = new uint256[](1); + scaledShares[0] = 1 ether; + + IDelegationManagerTypes.Withdrawal[] memory withdrawals = new IDelegationManagerTypes.Withdrawal[](1); + withdrawals[0] = IDelegationManagerTypes.Withdrawal({ + staker: address(0), + delegatedTo: address(0), + withdrawer: address(0), + nonce: 0, + startBlock: 0, + strategies: strategies, + scaledShares: scaledShares + }); + IERC20[][] memory tokens; bool[] memory receiveAsTokens; diff --git a/mainnet-contracts/test/unit/Timelock.t.sol b/mainnet-contracts/test/unit/Timelock.t.sol index 16fac6b3..77d22e1c 100644 --- a/mainnet-contracts/test/unit/Timelock.t.sol +++ b/mainnet-contracts/test/unit/Timelock.t.sol @@ -38,15 +38,6 @@ contract TimelockTest is Test { accessManager.canCall(caller, address(pufferVault), PufferVault.initiateETHWithdrawalsFromLido.selector); assertFalse(canCall, "should not be able to call"); - // Restricted to operations / community multisig - (canCall, delay) = accessManager.canCall(caller, address(pufferVault), PufferVault.depositToEigenLayer.selector); - assertFalse(canCall, "should not be able to call"); - - (canCall, delay) = accessManager.canCall( - caller, address(pufferVault), PufferVault.initiateStETHWithdrawalFromEigenLayer.selector - ); - assertFalse(canCall, "should not be able to call"); - // Upgrades are forbidden (canCall, delay) = accessManager.canCall(caller, address(pufferVault), UUPSUpgradeable.upgradeToAndCall.selector); From 29a6d3413e88fed5facd2f51bbeadc1c3750d2bd Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 10:40:55 +0100 Subject: [PATCH 15/37] remove unused code --- .../script/GenerateAccessManagerCallData.sol | 19 +--- mainnet-contracts/script/SetupAccess.s.sol | 47 ++++------ mainnet-contracts/src/PufferVaultStorage.sol | 6 +- mainnet-contracts/src/PufferVaultV2.sol | 93 ------------------- .../src/interface/IPufferVaultV2.sol | 24 ----- .../Integration/PufferTest.integration.t.sol | 2 - .../test/Integration/PufferVaultV2.fork.t.sol | 85 ----------------- .../test/unit/PufferProtocol.t.sol | 3 - .../test/unit/PufferWithdrawalManager.t.sol | 2 - 9 files changed, 25 insertions(+), 256 deletions(-) diff --git a/mainnet-contracts/script/GenerateAccessManagerCallData.sol b/mainnet-contracts/script/GenerateAccessManagerCallData.sol index 6a87ff35..15a31de5 100644 --- a/mainnet-contracts/script/GenerateAccessManagerCallData.sol +++ b/mainnet-contracts/script/GenerateAccessManagerCallData.sol @@ -21,14 +21,13 @@ import { PUBLIC_ROLE, ROLE_ID_DAO, ROLE_ID_PUFFER_PROTOCOL, ROLE_ID_OPERATIONS_M */ contract GenerateAccessManagerCallData is Script { function run(address pufferVaultProxy, address pufferDepositorProxy) public pure returns (bytes memory) { - bytes[] memory calldatas = new bytes[](5); + bytes[] memory calldatas = new bytes[](4); // Combine the two calldatas calldatas[0] = _getPublicSelectorsCalldata({ pufferVaultProxy: pufferVaultProxy }); - calldatas[1] = _getDaoSelectorsCalldataCalldata({ pufferVaultProxy: pufferVaultProxy }); - calldatas[2] = _getProtocolSelectorsCalldata({ pufferVaultProxy: pufferVaultProxy }); - calldatas[3] = _getOperationsSelectorsCalldata({ pufferVaultProxy: pufferVaultProxy }); - calldatas[4] = _getPublicSelectorsForDepositor({ pufferDepositorProxy: pufferDepositorProxy }); + calldatas[1] = _getProtocolSelectorsCalldata({ pufferVaultProxy: pufferVaultProxy }); + calldatas[2] = _getOperationsSelectorsCalldata({ pufferVaultProxy: pufferVaultProxy }); + calldatas[3] = _getPublicSelectorsForDepositor({ pufferDepositorProxy: pufferDepositorProxy }); bytes memory encodedMulticall = abi.encodeCall(Multicall.multicall, (calldatas)); @@ -52,16 +51,6 @@ contract GenerateAccessManagerCallData is Script { ); } - function _getDaoSelectorsCalldataCalldata(address pufferVaultProxy) internal pure returns (bytes memory) { - // DAO selectors - bytes4[] memory daoSelectors = new bytes4[](1); - daoSelectors[0] = PufferVaultV2.setDailyWithdrawalLimit.selector; - - return abi.encodeWithSelector( - AccessManager.setTargetFunctionRole.selector, pufferVaultProxy, daoSelectors, ROLE_ID_DAO - ); - } - function _getProtocolSelectorsCalldata(address pufferVaultProxy) internal pure returns (bytes memory) { // Puffer Protocol only // PufferProtocol will get `ROLE_ID_PUFFER_PROTOCOL` when it's deployed diff --git a/mainnet-contracts/script/SetupAccess.s.sol b/mainnet-contracts/script/SetupAccess.s.sol index d93abb53..af4118d9 100644 --- a/mainnet-contracts/script/SetupAccess.s.sol +++ b/mainnet-contracts/script/SetupAccess.s.sol @@ -86,7 +86,7 @@ contract SetupAccess is BaseScript { bytes[] memory coordinatorAccess, bytes[] memory validatorTicketAccess ) internal view returns (bytes[] memory calldatas) { - calldatas = new bytes[](31); + calldatas = new bytes[](30); calldatas[0] = _setupGuardianModuleRoles(); calldatas[1] = _setupEnclaveVerifierRoles(); calldatas[2] = rolesCalldatas[0]; @@ -105,27 +105,26 @@ contract SetupAccess is BaseScript { calldatas[13] = validatorTicketRoles[1]; calldatas[14] = vaultMainnetAccess[0]; - calldatas[15] = vaultMainnetAccess[1]; - calldatas[16] = pufferOracleAccess[0]; - calldatas[17] = pufferOracleAccess[1]; - calldatas[18] = pufferOracleAccess[2]; + calldatas[15] = pufferOracleAccess[0]; + calldatas[16] = pufferOracleAccess[1]; + calldatas[17] = pufferOracleAccess[2]; - calldatas[19] = moduleManagerAccess[0]; - calldatas[20] = moduleManagerAccess[1]; + calldatas[18] = moduleManagerAccess[0]; + calldatas[19] = moduleManagerAccess[1]; - calldatas[21] = roleLabels[0]; - calldatas[22] = roleLabels[1]; - calldatas[23] = roleLabels[2]; - calldatas[24] = roleLabels[3]; + calldatas[20] = roleLabels[0]; + calldatas[21] = roleLabels[1]; + calldatas[22] = roleLabels[2]; + calldatas[23] = roleLabels[3]; - calldatas[25] = coordinatorAccess[0]; - calldatas[26] = coordinatorAccess[1]; + calldatas[24] = coordinatorAccess[0]; + calldatas[25] = coordinatorAccess[1]; - calldatas[27] = validatorTicketAccess[0]; - calldatas[28] = validatorTicketAccess[1]; - calldatas[29] = validatorTicketAccess[2]; - calldatas[30] = validatorTicketAccess[3]; + calldatas[26] = validatorTicketAccess[0]; + calldatas[27] = validatorTicketAccess[1]; + calldatas[28] = validatorTicketAccess[2]; + calldatas[29] = validatorTicketAccess[3]; } function _labelRoles() internal pure returns (bytes[] memory) { @@ -219,22 +218,12 @@ contract SetupAccess is BaseScript { } function _setupPufferVaultMainnetAccess() internal view returns (bytes[] memory) { - bytes[] memory calldatas = new bytes[](2); - - bytes4[] memory operationsSelectors = new bytes4[](1); - operationsSelectors[0] = PufferVaultV2.setDailyWithdrawalLimit.selector; - - calldatas[0] = abi.encodeWithSelector( - AccessManager.setTargetFunctionRole.selector, - pufferDeployment.pufferVault, - operationsSelectors, - ROLE_ID_OPERATIONS_MULTISIG - ); + bytes[] memory calldatas = new bytes[](1); bytes4[] memory protocolSelectors = new bytes4[](1); protocolSelectors[0] = PufferVaultV2.transferETH.selector; - calldatas[1] = abi.encodeWithSelector( + calldatas[0] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, pufferDeployment.pufferVault, protocolSelectors, diff --git a/mainnet-contracts/src/PufferVaultStorage.sol b/mainnet-contracts/src/PufferVaultStorage.sol index f64829fc..c4635500 100644 --- a/mainnet-contracts/src/PufferVaultStorage.sol +++ b/mainnet-contracts/src/PufferVaultStorage.sol @@ -27,9 +27,9 @@ abstract contract PufferVaultStorage { EnumerableSet.Bytes32Set deprecated_eigenLayerWithdrawals; // Not in use anymore EnumerableMap.UintToUintMap lidoWithdrawalAmounts; // 1 Slot for daily withdrawal limits - uint96 dailyAssetsWithdrawalLimit; - uint96 assetsWithdrawnToday; - uint64 lastWithdrawalDay; + uint96 deprecated_dailyAssetsWithdrawalLimit; + uint96 deprecated_assetsWithdrawnToday; + uint64 deprecated_lastWithdrawalDay; // 1 slot for withdrawal fee uint256 exitFeeBasisPoints; // ETH rewards amount diff --git a/mainnet-contracts/src/PufferVaultV2.sol b/mainnet-contracts/src/PufferVaultV2.sol index fb82f3a0..8a8c4514 100644 --- a/mainnet-contracts/src/PufferVaultV2.sol +++ b/mainnet-contracts/src/PufferVaultV2.sol @@ -59,9 +59,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { PUFFER_ORACLE = oracle; ERC4626Storage storage erc4626Storage = _getERC4626StorageInternal(); erc4626Storage._asset = _WETH; - // This redundant code is for the Echidna fuzz testing - _setDailyWithdrawalLimit(100 ether); - _updateDailyWithdrawals(0); _setExitFeeBasisPoints(100); // 1% _disableInitializers(); } @@ -76,8 +73,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { // In this initialization, we swap out the underlying stETH with WETH ERC4626Storage storage erc4626Storage = _getERC4626StorageInternal(); erc4626Storage._asset = _WETH; - _setDailyWithdrawalLimit(100 ether); - _updateDailyWithdrawals(0); _setExitFeeBasisPoints(100); // 1% // Return pufETH to Puffers @@ -144,8 +139,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets); } - _updateDailyWithdrawals(assets); - _wrapETH(assets); uint256 shares = previewWithdraw(assets); @@ -179,8 +172,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { uint256 assets = previewRedeem(shares); - _updateDailyWithdrawals(assets); - _wrapETH(assets); _withdraw({ caller: _msgSender(), receiver: receiver, owner: owner, assets: assets, shares: shares }); @@ -370,16 +361,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { return _convertToShares(assets, Math.Rounding.Ceil); } - /** - * @notice Sets a new daily withdrawal limit - * @dev Restricted to the DAO - * @param newLimit The new daily limit to be set - */ - function setDailyWithdrawalLimit(uint96 newLimit) external restricted { - _setDailyWithdrawalLimit(newLimit); - _resetDailyWithdrawals(); - } - /** * @param newExitFeeBasisPoints is the new exit fee basis points * @dev Restricted to the DAO @@ -388,48 +369,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { _setExitFeeBasisPoints(newExitFeeBasisPoints); } - /** - * @inheritdoc IPufferVaultV2 - */ - function getRemainingAssetsDailyWithdrawalLimit() public view virtual returns (uint256) { - VaultStorage storage $ = _getPufferVaultStorage(); - uint96 dailyAssetsWithdrawalLimit = $.dailyAssetsWithdrawalLimit; - uint96 assetsWithdrawnToday = $.assetsWithdrawnToday; - - // If we are in a new day, return the full daily limit - if ($.lastWithdrawalDay < block.timestamp / 1 days) { - return dailyAssetsWithdrawalLimit; - } - - return dailyAssetsWithdrawalLimit - assetsWithdrawnToday; - } - - /** - * @notice Calculates the maximum amount of assets (WETH) that can be withdrawn by the `owner`. - * @dev This function considers both the remaining daily withdrawal limit and the `owner`'s balance. - * See {IERC4626-maxWithdraw} - * @param owner The address of the owner for which the maximum withdrawal amount is calculated. - * @return maxAssets The maximum amount of assets that can be withdrawn by the `owner`. - */ - function maxWithdraw(address owner) public view virtual override returns (uint256 maxAssets) { - uint256 remainingAssets = getRemainingAssetsDailyWithdrawalLimit(); - uint256 maxUserAssets = previewRedeem(balanceOf(owner)); - return remainingAssets < maxUserAssets ? remainingAssets : maxUserAssets; - } - - /** - * @notice Calculates the maximum amount of shares (pufETH) that can be redeemed by the `owner`. - * @dev This function considers both the remaining daily withdrawal limit in terms of assets and converts it to shares, and the `owner`'s share balance. - * See {IERC4626-maxRedeem} - * @param owner The address of the owner for which the maximum redeemable shares are calculated. - * @return maxShares The maximum amount of shares that can be redeemed by the `owner`. - */ - function maxRedeem(address owner) public view virtual override returns (uint256 maxShares) { - uint256 remainingShares = previewWithdraw(getRemainingAssetsDailyWithdrawalLimit()); - uint256 userShares = balanceOf(owner); - return remainingShares < userShares ? remainingShares : userShares; - } - /** * @dev Preview adding an exit fee on withdraw. See {IERC4626-previewWithdraw}. */ @@ -483,31 +422,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { } } - /** - * @notice Updates the amount of assets (WETH) withdrawn today - * @param withdrawalAmount is the assets (WETH) amount - */ - function _updateDailyWithdrawals(uint256 withdrawalAmount) internal virtual { - VaultStorage storage $ = _getPufferVaultStorage(); - - // Check if it's a new day to reset the withdrawal count - if ($.lastWithdrawalDay < block.timestamp / 1 days) { - _resetDailyWithdrawals(); - } - $.assetsWithdrawnToday += uint96(withdrawalAmount); - emit AssetsWithdrawnToday($.assetsWithdrawnToday); - } - - /** - * @notice Updates the maximum amount of assets (WETH) that can be withdrawn daily - * @param newLimit is the assets (WETH) amount - */ - function _setDailyWithdrawalLimit(uint96 newLimit) internal virtual { - VaultStorage storage $ = _getPufferVaultStorage(); - emit DailyWithdrawalLimitSet($.dailyAssetsWithdrawalLimit, newLimit); - $.dailyAssetsWithdrawalLimit = newLimit; - } - /** * @notice Updates the exit fee basis points * @dev 200 Basis points = 2% is the maximum exit fee @@ -542,13 +456,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { _; } - function _resetDailyWithdrawals() internal virtual { - VaultStorage storage $ = _getPufferVaultStorage(); - $.lastWithdrawalDay = uint64(block.timestamp / 1 days); - $.assetsWithdrawnToday = 0; - emit DailyWithdrawalLimitReset(); - } - function _authorizeUpgrade(address newImplementation) internal virtual override restricted { } function _getERC4626StorageInternal() private pure returns (ERC4626Storage storage $) { diff --git a/mainnet-contracts/src/interface/IPufferVaultV2.sol b/mainnet-contracts/src/interface/IPufferVaultV2.sol index 5b19d46e..fcdc909a 100644 --- a/mainnet-contracts/src/interface/IPufferVaultV2.sol +++ b/mainnet-contracts/src/interface/IPufferVaultV2.sol @@ -24,24 +24,6 @@ interface IPufferVaultV2 is IPufferVault { */ error InvalidExitFeeBasisPoints(); - /** - * Emitted when assets (WETH) are withdrawn - * @dev Signature: 0x139f9ee0762f3b0c92a4b8c7b8fe8be6b12aaece4b9b22de6bf1ba1094dcd998 - */ - event AssetsWithdrawnToday(uint256 withdrawalAmount); - - /** - * Emitted daily withdrawal limit is reset - * @dev Signature: 0x190567136e3dd93d29bef98a7c7c87cff34ee88e71d634b52f5fb3b531085f40 - */ - event DailyWithdrawalLimitReset(); - - /** - * Emitted when the daily withdrawal limit is set - * @dev Signature: 0x8d5f7487ce1fd25059bd15204a55ea2c293160362b849a6f9244aec7d5a3700b - */ - event DailyWithdrawalLimitSet(uint96 oldLimit, uint96 newLimit); - /** * Emitted when the Vault transfers ETH to a specified address * @dev Signature: 0xba7bb5aa419c34d8776b86cc0e9d41e72d74a893a511f361a11af6c05e920c3d @@ -65,12 +47,6 @@ interface IPufferVaultV2 is IPufferVault { */ function getExitFeeBasisPoints() external view returns (uint256); - /** - * @notice Returns the remaining assets that can be withdrawn today - * @return The remaining assets that can be withdrawn today - */ - function getRemainingAssetsDailyWithdrawalLimit() external view returns (uint256); - /** * @notice Deposits native ETH into the Puffer Vault * @param receiver The recipient of pufETH tokens diff --git a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol index 49f26eaf..b1f4273f 100644 --- a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol +++ b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol @@ -279,8 +279,6 @@ contract PufferTest is Test { assertEq(pufferVault.totalAssets(), assetsBefore + 10 ether, "Previous assets should increase"); - PufferVaultV2(payable(address(pufferVault))).getRemainingAssetsDailyWithdrawalLimit(); - pufferVault.balanceOf(eve); uint256 maxWithdraw = pufferVault.maxWithdraw(eve); diff --git a/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol index fc579134..358da6e6 100644 --- a/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol +++ b/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol @@ -50,7 +50,6 @@ contract PufferVaultV2ForkTest is MainnetForkTestHelper { assertEq(pufferVault.asset(), address(_WETH), "asset"); assertEq(pufferVault.getPendingLidoETHAmount(), 0, "0 pending lido eth"); assertEq(pufferVault.totalAssets(), 368072.286049064583783628 ether, "total assets"); - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 100 ether, "daily withdrawal limit"); assertEq(pufferVault.getExitFeeBasisPoints(), 100, "1% withdrawal fee"); } @@ -138,38 +137,6 @@ contract PufferVaultV2ForkTest is MainnetForkTestHelper { assertEq(pufferVault.maxRedeem(pufferWhale), 100.595147442558494386 ether, "max redeem"); } - function test_setDailyWithdrawalLimit() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - address dao = makeAddr("dao"); - - // Grant DAO role to 'dao' address - vm.startPrank(address(timelock)); - accessManager.grantRole(ROLE_ID_DAO, dao, 0); - - // Whale has more than 100 ether, but the limit is 100 eth - assertEq(pufferVault.maxWithdraw(pufferWhale), 100 ether, "max withdraw"); - - vm.startPrank(pufferWhale); - pufferVault.withdraw(pufferVault.maxWithdraw(pufferWhale), pufferWhale, pufferWhale); - - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 0, "remaining assets daily withdrawal limit"); - - // Set the new limit - uint96 newLimit = 1000 ether; - vm.startPrank(dao); - vm.expectEmit(true, true, true, true); - emit IPufferVaultV2.DailyWithdrawalLimitReset(); - pufferVault.setDailyWithdrawalLimit(newLimit); - - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), newLimit, "daily withdrawal limit"); - // Shares amount - uint256 maxRedeem = pufferVault.maxRedeem(pufferWhale); - // If we convert shares to assets, it should be equal to the new limit + 10 (1% is the withdrawal fee) - assertEq(pufferVault.convertToAssets(maxRedeem), 1010 ether, "max redeem converted to assets"); - } - function test_withdraw_fee() public { // Get withdrawal liquidity _withdraw_stETH_from_lido(); @@ -238,32 +205,6 @@ contract PufferVaultV2ForkTest is MainnetForkTestHelper { uint256 recipientBalance = _WETH.balanceOf(recipient); assertGt(recipientBalance, 20 ether, "+10 weth"); - - // Assert the daily withdrawal limit - assertEq( - pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 100 ether - recipientBalance, "daily withdrawal limit" - ); - } - - function test_daily_limit_reset() public { - _withdraw_stETH_from_lido(); - - vm.startPrank(pufferWhale); - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 100 ether, "daily withdrawal limit"); - - assertEq(pufferVault.maxWithdraw(pufferWhale), 100 ether, "max withdraw"); - pufferVault.withdraw(50 ether, pufferWhale, pufferWhale); - - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 50 ether, "daily withdrawal limit reduced"); - - vm.warp(block.timestamp + 1 days); - - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 100 ether, "daily withdrawal limit reduced"); - - assertEq(pufferVault.maxWithdraw(pufferWhale), 100 ether, "max withdraw"); - pufferVault.withdraw(22 ether, pufferWhale, pufferWhale); - - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 78 ether, "daily withdrawal limit reduced"); } function test_withdrawal() public { @@ -271,17 +212,14 @@ contract PufferVaultV2ForkTest is MainnetForkTestHelper { _withdraw_stETH_from_lido(); vm.startPrank(pufferWhale); - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 100 ether, "daily withdrawal limit"); assertEq(pufferVault.maxWithdraw(pufferWhale), 100 ether, "max withdraw"); pufferVault.withdraw(50 ether, pufferWhale, pufferWhale); - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 50 ether, "daily withdrawal limit reduced"); assertEq(pufferVault.maxWithdraw(pufferWhale), 50 ether, "leftover max withdraw"); pufferVault.withdraw(50 ether, pufferWhale, pufferWhale); assertEq(pufferVault.maxWithdraw(pufferWhale), 0 ether, "no leftover max withdraw"); - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 0 ether, "everything withdrawn"); } function test_withdrawal_transfers_to_receiver() public { @@ -385,29 +323,6 @@ contract PufferVaultV2ForkTest is MainnetForkTestHelper { pufferVault.depositStETH(100 ether + 1, alice); } - function test_change_withdrawal_limit() public { - _withdraw_stETH_from_lido(); - - address dao = makeAddr("dao"); - - // Grant DAO role to 'dao' address - vm.startPrank(address(timelock)); - accessManager.grantRole(ROLE_ID_DAO, dao, 0); - - vm.startPrank(pufferWhale); - pufferVault.withdraw(20 ether, pufferWhale, pufferWhale); - - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 80 ether, "daily withdrawal limit"); - - // Set the new limit - uint96 newLimit = 10 ether; - vm.startPrank(dao); - pufferVault.setDailyWithdrawalLimit(newLimit); - - // The remaining limit is reset in `setDailyWithdrawalLimit` - assertEq(pufferVault.getRemainingAssetsDailyWithdrawalLimit(), 10 ether, "10 ether left - limit is reset"); - } - function test_burn() public withCaller(pufferWhale) { vm.expectRevert(); pufferVault.burn(100 ether); diff --git a/mainnet-contracts/test/unit/PufferProtocol.t.sol b/mainnet-contracts/test/unit/PufferProtocol.t.sol index 1142eb0a..192befdf 100644 --- a/mainnet-contracts/test/unit/PufferProtocol.t.sol +++ b/mainnet-contracts/test/unit/PufferProtocol.t.sol @@ -68,9 +68,6 @@ contract PufferProtocolTest is UnitTestHelper { accessManager.grantRole(ROLE_ID_OPERATIONS_MULTISIG, address(this), 0); vm.stopPrank(); - // Set daily withdrawals limit - pufferVault.setDailyWithdrawalLimit(1000 ether); - _skipDefaultFuzzAddresses(); fuzzedAddressMapping[address(pufferProtocol)] = true; diff --git a/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol b/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol index bb8ab337..f6bdc902 100644 --- a/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol +++ b/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol @@ -697,8 +697,6 @@ contract PufferWithdrawalManagerTest is UnitTestHelper { function test_funds_returning_edge_case() public { assertEq(pufferVault.totalSupply(), 1000 ether, "totalSupply should be 1000 ETH"); - vm.startPrank(address(DAO)); - pufferVault.setDailyWithdrawalLimit(type(uint96).max); vm.startPrank(address(timelock)); pufferVault.setExitFeeBasisPoints(0); From 5a52fc36f6fbcda6240a5c30af1ed437561a4dad Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 11:28:40 +0100 Subject: [PATCH 16/37] remove unused code --- mainnet-contracts/src/PufferVault.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/mainnet-contracts/src/PufferVault.sol b/mainnet-contracts/src/PufferVault.sol index fd62478a..02f4e7a6 100644 --- a/mainnet-contracts/src/PufferVault.sol +++ b/mainnet-contracts/src/PufferVault.sol @@ -30,9 +30,7 @@ contract PufferVault is ERC4626Upgradeable, UUPSUpgradeable { - using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableSet for EnumerableSet.UintSet; - using SafeERC20 for address; /** * @dev stETH contract From d6a6c5d7ad1e6b948100d20a92dd33aecc1d5078 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 12:20:13 +0100 Subject: [PATCH 17/37] remove more useless code --- mainnet-contracts/script/DeployPufETH.s.sol | 26 +- mainnet-contracts/src/PufferVault.sol | 72 --- mainnet-contracts/src/PufferVaultV2.sol | 5 +- .../Integration/PufferTest.integration.t.sol | 463 ------------------ mainnet-contracts/test/unit/PufETH.t.sol | 4 - .../test/unit/PufferVaultV2Property.t.sol | 100 ---- mainnet-contracts/test/unit/Timelock.t.sol | 6 +- 7 files changed, 6 insertions(+), 670 deletions(-) delete mode 100644 mainnet-contracts/test/Integration/PufferTest.integration.t.sol delete mode 100644 mainnet-contracts/test/unit/PufferVaultV2Property.t.sol diff --git a/mainnet-contracts/script/DeployPufETH.s.sol b/mainnet-contracts/script/DeployPufETH.s.sol index babda705..e92ecd19 100644 --- a/mainnet-contracts/script/DeployPufETH.s.sol +++ b/mainnet-contracts/script/DeployPufETH.s.sol @@ -200,21 +200,13 @@ contract DeployPufETH is BaseScript { } function _setupOther() internal view returns (bytes[] memory) { - bytes[] memory calldatas = new bytes[](5); - - bytes4[] memory selectors = new bytes4[](1); - selectors[0] = PufferVault.initiateETHWithdrawalsFromLido.selector; - - // Setup setup role - calldatas[0] = abi.encodeWithSelector( - AccessManager.setTargetFunctionRole.selector, address(vaultProxy), selectors, ROLE_ID_OPERATIONS_MULTISIG - ); + bytes[] memory calldatas = new bytes[](3); // Setup role members (no delay) - calldatas[1] = + calldatas[0] = abi.encodeWithSelector(AccessManager.grantRole.selector, ROLE_ID_OPERATIONS_MULTISIG, operationsMultisig, 0); // Grant admin role to timelock - calldatas[2] = + calldatas[1] = abi.encodeWithSelector(AccessManager.grantRole.selector, accessManager.ADMIN_ROLE(), address(timelock), 0); // Setup public access for PufferDepositor @@ -226,20 +218,10 @@ contract DeployPufETH is BaseScript { publicSelectors[4] = PufferDepositor.swapAndDeposit1Inch.selector; publicSelectors[5] = PufferDepositor.depositStETH.selector; - calldatas[3] = abi.encodeCall( + calldatas[2] = abi.encodeCall( AccessManager.setTargetFunctionRole, (address(depositorProxy), publicSelectors, accessManager.PUBLIC_ROLE()) ); - // Setup public access for PufferVault - bytes4[] memory publicSelectorsPufferVault = new bytes4[](2); - publicSelectorsPufferVault[0] = PufferVault.deposit.selector; - publicSelectorsPufferVault[1] = PufferVault.mint.selector; - - calldatas[4] = abi.encodeCall( - AccessManager.setTargetFunctionRole, - (address(vaultProxy), publicSelectorsPufferVault, accessManager.PUBLIC_ROLE()) - ); - return calldatas; } diff --git a/mainnet-contracts/src/PufferVault.sol b/mainnet-contracts/src/PufferVault.sol index 02f4e7a6..b6f54e3f 100644 --- a/mainnet-contracts/src/PufferVault.sol +++ b/mainnet-contracts/src/PufferVault.sol @@ -5,7 +5,6 @@ import { IPufferVault } from "./interface/IPufferVault.sol"; import { IStETH } from "./interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; import { PufferVaultStorage } from "./PufferVaultStorage.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -70,47 +69,6 @@ contract PufferVault is } } - /** - * @inheritdoc ERC4626Upgradeable - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol - */ - function deposit(uint256 assets, address receiver) public virtual override restricted returns (uint256) { - return super.deposit(assets, receiver); - } - - /** - * @inheritdoc ERC4626Upgradeable - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol - */ - function mint(uint256 shares, address receiver) public virtual override restricted returns (uint256) { - return super.mint(shares, receiver); - } - - /** - * @notice Claims ETH withdrawals from Lido - * @param requestIds An array of request IDs for the withdrawals - */ - function claimWithdrawalsFromLido(uint256[] calldata requestIds) external virtual { - VaultStorage storage $ = _getPufferVaultStorage(); - - // Tell our receive() that we are doing a Lido claim - $.deprecated_isLidoWithdrawal = true; - - for (uint256 i = 0; i < requestIds.length; ++i) { - bool isValidWithdrawal = $.lidoWithdrawals.remove(requestIds[i]); - if (!isValidWithdrawal) { - revert InvalidWithdrawal(); - } - - // slither-disable-next-line calls-loop - _LIDO_WITHDRAWAL_QUEUE.claimWithdrawal(requestIds[i]); - } - - // Reset back the value - $.deprecated_isLidoWithdrawal = false; - emit ClaimedWithdrawals(requestIds); - } - /** * @dev See {IERC4626-totalAssets}. * Eventually, stETH will not be part of this vault anymore, and the Vault(pufETH) will represent shares of total ETH holdings @@ -132,36 +90,6 @@ contract PufferVault is return $.lidoLockedETH; } - /** - * @notice Initiates ETH withdrawals from Lido - * Restricted access - * @param amounts An array of amounts that we want to queue - */ - function initiateETHWithdrawalsFromLido(uint256[] calldata amounts) - external - virtual - restricted - returns (uint256[] memory requestIds) - { - VaultStorage storage $ = _getPufferVaultStorage(); - - uint256 lockedAmount; - for (uint256 i = 0; i < amounts.length; ++i) { - lockedAmount += amounts[i]; - } - $.lidoLockedETH += lockedAmount; - - SafeERC20.safeIncreaseAllowance(_ST_ETH, address(_LIDO_WITHDRAWAL_QUEUE), lockedAmount); - requestIds = _LIDO_WITHDRAWAL_QUEUE.requestWithdrawals(amounts, address(this)); - - // nosemgrep array-length-outside-loop - for (uint256 i = 0; i < requestIds.length; ++i) { - $.lidoWithdrawals.add(requestIds[i]); - } - emit RequestedWithdrawals(requestIds); - return requestIds; - } - /** * @notice Required by the ERC721 Standard */ diff --git a/mainnet-contracts/src/PufferVaultV2.sol b/mainnet-contracts/src/PufferVaultV2.sol index 8a8c4514..28bc0f89 100644 --- a/mainnet-contracts/src/PufferVaultV2.sol +++ b/mainnet-contracts/src/PufferVaultV2.sol @@ -227,7 +227,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { } /** - * @inheritdoc PufferVault * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol */ function deposit(uint256 assets, address receiver) @@ -242,7 +241,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { } /** - * @inheritdoc PufferVault * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol */ function mint(uint256 shares, address receiver) public virtual override markDeposit restricted returns (uint256) { @@ -258,7 +256,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { function initiateETHWithdrawalsFromLido(uint256[] calldata amounts) external virtual - override restricted returns (uint256[] memory requestIds) { @@ -287,7 +284,7 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { * @dev Restricted to Operations Multisig * @param requestIds An array of request IDs for the withdrawals */ - function claimWithdrawalsFromLido(uint256[] calldata requestIds) external virtual override restricted { + function claimWithdrawalsFromLido(uint256[] calldata requestIds) external virtual restricted { require(requestIds.length != 0); VaultStorage storage $ = _getPufferVaultStorage(); diff --git a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol b/mainnet-contracts/test/Integration/PufferTest.integration.t.sol deleted file mode 100644 index b1f4273f..00000000 --- a/mainnet-contracts/test/Integration/PufferTest.integration.t.sol +++ /dev/null @@ -1,463 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { Test } from "forge-std/Test.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { PufferDepositor } from "../../src/PufferDepositor.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; -import { PufferVaultV2Tests } from "../../test/mocks/PufferVaultV2Tests.sol"; -import { PufferDepositorV2 } from "../../src/PufferDepositorV2.sol"; -import { IStETH } from "../../src/interface/Lido/IStETH.sol"; -import { MockPufferOracle } from "../mocks/MockPufferOracle.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IPufferVault } from "../../src/interface/IPufferVault.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import { DeployPufETH } from "script/DeployPufETH.s.sol"; -import { PufferDeployment } from "../../src/structs/PufferDeployment.sol"; -import { stdStorage, StdStorage } from "forge-std/Test.sol"; -import { PufferVault } from "../../src/PufferVault.sol"; -import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; -import { IStETH } from "../../src/interface/Lido/IStETH.sol"; -import { IWstETH } from "../../src/interface/Lido/IWstETH.sol"; -import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { Timelock } from "../../src/Timelock.sol"; -import { IWETH } from "../../src/interface/Other/IWETH.sol"; -import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; -import { Permit } from "../../src/structs/Permit.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; - -/** - * @dev PufferDepositor and PufferVault tests (v1) - */ -contract PufferTest is Test { - /** - * @dev Ethereum Mainnet addresses - */ - IStrategy internal constant _EIGEN_STETH_STRATEGY = IStrategy(0x93c4b944D05dfe6df7645A86cd2206016c51564D); - IDelegationManager internal constant _EIGEN_DELEGATION_MANGER = - IDelegationManager(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); - IEigenLayer internal constant _EIGEN_STRATEGY_MANAGER = IEigenLayer(0x858646372CC42E1A627fcE94aa7A7033e7CF075A); - IStETH internal constant _ST_ETH = IStETH(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84); - IWETH internal constant _WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - IWstETH internal constant _WST_ETH = IWstETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0); - ILidoWithdrawalQueue internal constant _LIDO_WITHDRAWAL_QUEUE = - ILidoWithdrawalQueue(0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1); - - using stdStorage for StdStorage; - - bytes32 private constant _PERMIT_TYPEHASH = - keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - - struct _TestTemps { - address owner; - address to; - uint256 amount; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - uint256 privateKey; - uint256 nonce; - bytes32 domainSeparator; - } - - PufferDepositor public pufferDepositor; - PufferVault public pufferVault; - AccessManager public accessManager; - Timelock public timelock; - - // Lido contract (stETH) - IStETH stETH = IStETH(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84); - // EL Strategy Manager - IEigenLayer eigenStrategyManager = IEigenLayer(0x858646372CC42E1A627fcE94aa7A7033e7CF075A); - - address alice = makeAddr("alice"); - // Bob.. - address bob; - uint256 bobSK; - address charlie = makeAddr("charlie"); - address dave = makeAddr("dave"); - address eve = makeAddr("eve"); - - // Use Maker address for mainnet fork tests to get wETH - address MAKER_VAULT = 0x2F0b23f53734252Bda2277357e97e1517d6B042A; - // Use Blast deposit contract for mainnet fork tests to get stETH - address BLAST_DEPOSIT = 0x5F6AE08B8AeB7078cf2F96AFb089D7c9f51DA47d; - - address LIDO_WITHDRAWAL_QUEUE = 0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1; - address LIDO_ACCOUNTING_ORACLE = 0x852deD011285fe67063a08005c71a85690503Cee; - - // Storage slot for the Consensus Layer Balance in stETH - bytes32 internal constant CL_BALANCE_POSITION = 0xa66d35f054e68143c18f32c990ed5cb972bb68a68f500cd2dd3a16bbf3686483; // keccak256("lido.Lido.beaconBalance"); - - address COMMUNITY_MULTISIG; - address OPERATIONS_MULTISIG; - - function setUp() public { - // By forking to block 18812842, tests will start BEFORE the Lido oracle rebase - // Lido oracle has some checks, and due to those checks when we want to rebase will roll the block number to 18819958 - vm.createSelectFork(vm.rpcUrl("mainnet"), 18812842); - - // Deploy the contracts on the fork above - _setupContracts(); - } - - function _setupContracts() internal { - PufferDeployment memory deployment = new DeployPufETH().run(); - pufferDepositor = PufferDepositor(payable(deployment.pufferDepositor)); - pufferVault = PufferVault(payable(deployment.pufferVault)); - accessManager = AccessManager(payable(deployment.accessManager)); - timelock = Timelock(payable(deployment.timelock)); - - COMMUNITY_MULTISIG = timelock.COMMUNITY_MULTISIG(); - OPERATIONS_MULTISIG = timelock.OPERATIONS_MULTISIG(); - - vm.label(COMMUNITY_MULTISIG, "COMMUNITY_MULTISIG"); - vm.label(OPERATIONS_MULTISIG, "OPERATIONS_MULTISIG"); - vm.label(address(stETH), "stETH"); - vm.label(MAKER_VAULT, "MAKER Vault"); - vm.label(0x93c4b944D05dfe6df7645A86cd2206016c51564D, "Eigen stETH strategy"); - - (bob, bobSK) = makeAddrAndKey("bob"); - } - - // Transfer `token` from `from` to `to` to fill accounts in mainnet fork tests - modifier giveToken(address from, address token, address to, uint256 amount) { - vm.startPrank(from); - SafeERC20.safeTransfer(IERC20(token), to, amount); - vm.stopPrank(); - _; - } - - modifier withCaller(address caller) { - vm.startPrank(caller); - _; - vm.stopPrank(); - } - - function _increaseELstETHCap() public { - // An example of EL increasing the cap - // We copied the callldata from this transaction to simulate it - // https://etherscan.io/tx/0xc16610a3dc3e8732e3fbb7761f6e1c0e44869cba5a41b058d2b3abce98833667 - vm.startPrank(0xe7fFd467F7526abf9c8796EDeE0AD30110419127); // EL - (bool success,) = 0xBE1685C81aA44FF9FB319dD389addd9374383e90.call( // El Multisig - hex"6a761202000000000000000000000000a6db1a8c5a981d1536266d2a393c5f8ddb210eaf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000005c40825f38f000000000000000000000000369e6f597e22eab55ffb173c6d9cd234bd699111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000657eb4f30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a46a76120200000000000000000000000040a2accbd92bca938b02010e17a5b8929b49130d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000002a48d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002440093c4b944d05dfe6df7645a86cd2206016c51564d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004411c70c9d000000000000000000000000000000000000000000002a5a058fc295ed000000000000000000000000000000000000000000000000002a5a058fc295ed000000001bee69b7dfffa4e2d53c2a2df135c388ad25dcd20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004411c70c9d000000000000000000000000000000000000000000002a5a058fc295ed000000000000000000000000000000000000000000000000002a5a058fc295ed0000000054945180db7943c0ed0fee7edab2bd24620256bc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004411c70c9d000000000000000000000000000000000000000000002a5a058fc295ed000000000000000000000000000000000000000000000000002a5a058fc295ed00000000858646372cc42e1a627fce94aa7a7033e7cf075a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024fabc1cbc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041000000000000000000000000a6db1a8c5a981d1536266d2a393c5f8ddb210eaf00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c30b32ae3865c0fd6cc396243889688a34f95c45a9110fe0aadc60b2a6e99e383d5d67668ffa2f5481f0003d26a5aa6b07746dd6b6162db411c585f31483efd6961b000000000000000000000000e7ffd467f7526abf9c8796edee0ad30110419127000000000000000000000000000000000000000000000000000000000000000001e3d807e6e26f9702b76782c559ef94158f44da655c8eb4e5d26f1e7cea4ef6287fa6b6da3baae46e6f8da28111d64ab62e07a0f4b80d3e418e1f8b89d62b34621c0000000000000000000000000000000000000000000000000000000000" - ); - assertTrue(success, "oracle rebase failed"); - vm.stopPrank(); - } - - function _rebaseLido() internal { - // Simulates stETH rebasing by fast-forwarding block 18819958 where Lido oracle rebased. // Submits the same call data as the Lido oracle. - // https://etherscan.io/tx/0xc308f3173b7a73b62751c42b5349203fa2684ad9b977cac5daf74582ff87d9ab - vm.roll(18819958); - vm.startPrank(0x140Bd8FbDc884f48dA7cb1c09bE8A2fAdfea776E); // Lido's whitelisted Oracle - (bool success,) = LIDO_ACCOUNTING_ORACLE.call( - hex"fc7377cd00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000007a2aff000000000000000000000000000000000000000000000000000000000004b6bb00000000000000000000000000000000000000000000000000207cc3840da37700000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000291edebdc938e7a00000000000000000000000000000000000000000000000000d37c862e1201902f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000003b7c24bbc12e7a67c59354500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001af7a147aadae04565041a10836ae2210426a05e5e4d60834a4d8ebc716f2948c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000060cb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000004918" - ); - assertTrue(success, "oracle rebase failed"); - vm.stopPrank(); - } - - function _finalizeWithdrawals(uint256 requestIdFinalized) internal { - // Alter WithdrawalRouter storage slot to mark our withdrawal requests as finalized - vm.store( - LIDO_WITHDRAWAL_QUEUE, - keccak256("lido.WithdrawalQueue.lastFinalizedRequestId"), - bytes32(uint256(requestIdFinalized)) - ); - } - - function _upgradeToMainnetPuffer() internal { - MockPufferOracle mockOracle = new MockPufferOracle(); - - // Simulate that our deployed oracle becomes active and starts posting results of Puffer staking - // At this time, we stop accepting stETH, and we accept only native ETH - PufferVaultV2 newImplementation = new PufferVaultV2Tests(_ST_ETH, _WETH, _LIDO_WITHDRAWAL_QUEUE, mockOracle); - - // Community multisig can do thing instantly - vm.startPrank(COMMUNITY_MULTISIG); - - //Upgrade PufferVault - vm.expectEmit(true, true, true, true); - emit Initializable.Initialized(2); - UUPSUpgradeable(pufferVault).upgradeToAndCall( - address(newImplementation), abi.encodeCall(PufferVaultV2.initialize, ()) - ); - - PufferDepositorV2 newDepositorImplementation = - new PufferDepositorV2(PufferVaultV2(payable(pufferVault)), _ST_ETH); - - // Upgrade PufferDepositor - emit Initializable.Initialized(2); - - (bool success,) = address(timelock).call( - abi.encodeWithSelector( - Timelock.executeTransaction.selector, - address(pufferDepositor), - abi.encodeCall(UUPSUpgradeable.upgradeToAndCall, (address(newDepositorImplementation), "")), - 1 - ) - ); - require(success, "failed upgrade tx"); - - // Setup access - bytes memory encodedMulticall = - new GenerateAccessManagerCallData().run(address(pufferVault), address(pufferDepositor)); - // Timelock is the owner of the AccessManager - (success,) = address(timelock).call( - abi.encodeWithSelector(Timelock.executeTransaction.selector, address(accessManager), encodedMulticall, 1) - ); - require(success, "failed upgrade tx"); - - vm.stopPrank(); - } - - function test_lido_withdrawal_dos() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 1 ether) // Blast got a lot of stETH - giveToken(BLAST_DEPOSIT, address(stETH), address(pufferVault), 2000 ether) // Blast got a lot of stETH - withCaller(alice) - { - // Alice queues a withdrawal directly on Lido and sets the PufferVault as the recipient - uint256[] memory aliceAmounts = new uint256[](1); - aliceAmounts[0] = 1 ether; - - SafeERC20.safeIncreaseAllowance(_ST_ETH, address(_LIDO_WITHDRAWAL_QUEUE), 1 ether); - uint256[] memory aliceRequestIds = _LIDO_WITHDRAWAL_QUEUE.requestWithdrawals(aliceAmounts, address(pufferVault)); - - // Queue 2x 1000 ETH withdrawals on Lido - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000 ether; // steth Amount - amounts[1] = 1000 ether; // steth Amount - vm.startPrank(OPERATIONS_MULTISIG); - uint256[] memory requestIds = pufferVault.initiateETHWithdrawalsFromLido(amounts); - - // Finalize all 3 withdrawals and fast forward to +10 days - _finalizeWithdrawals(requestIds[1]); - vm.roll(block.number + 10 days); - - // We try to claim the withdrawal that wasn't requested through the PufferVault - vm.expectRevert(IPufferVault.InvalidWithdrawal.selector); - pufferVault.claimWithdrawalsFromLido(aliceRequestIds); - - // This one should work - pufferVault.claimWithdrawalsFromLido(requestIds); - - // 0.01% is the max delta - assertApproxEqRel(address(pufferVault).balance, 2000 ether, 0.0001e18, "oh no"); - } - - function test_zero_stETH_deposit() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 1000 ether) // Blast got a lot of stETH - withCaller(alice) - { - SafeERC20.safeIncreaseAllowance(IERC20(_ST_ETH), address(pufferVault), type(uint256).max); - - uint256 minted = pufferVault.deposit(0, alice); - assertEq(minted, 0, "got 0 back"); - } - - function test_upgrade_to_mainnet() public giveToken(MAKER_VAULT, address(_WETH), eve, 10 ether) { - // Test pre-mainnet version - test_minting_and_lido_rebasing(); - - uint256 assetsBefore = pufferVault.totalAssets(); - - // Upgrade to mainnet - _upgradeToMainnetPuffer(); - - vm.startPrank(eve); - SafeERC20.safeIncreaseAllowance(_WETH, address(pufferVault), type(uint256).max); - - uint256 pufETHMinted = pufferVault.deposit(10 ether, eve); - - assertEq(pufferVault.totalAssets(), assetsBefore + 10 ether, "Previous assets should increase"); - - pufferVault.balanceOf(eve); - uint256 maxWithdraw = pufferVault.maxWithdraw(eve); - - uint256 assetsValue = pufferVault.convertToAssets(pufETHMinted); - assertApproxEqAbs(assetsValue, 10 ether, 1, "convertToAssets matches the original deposited amount"); - - // IERC4626 natspec says: - /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in - /// share price or some other type of condition, meaning the depositor will lose assets by redeeming. - - assertLt(maxWithdraw, pufETHMinted, "max withdraw should is smaller because of the withdrawal fee"); - - pufferVault.withdraw(maxWithdraw, eve, eve); - - // Alice got less than she deposited ~ -1% less - assertEq(_WETH.balanceOf(eve), 9.900990099009900989 ether, "eve weth after withdrawal"); - - // Deposited 10 ETH, got back ~9.9 ETH - uint256 assetsDif = 10 ether - _WETH.balanceOf(eve); - - // The rest stays in the vault - assertEq( - pufferVault.totalAssets(), assetsBefore + assetsDif, "should have a little more because alice got 1% less" - ); - } - - function test_minting_and_lido_rebasing() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 1000 ether) // Blast got a lot of stETH - giveToken(BLAST_DEPOSIT, address(stETH), bob, 1000 ether) - { - // Pretend that alice is depositing 1k ETH - vm.startPrank(alice); - SafeERC20.safeIncreaseAllowance(IERC20(stETH), address(pufferVault), type(uint256).max); - uint256 aliceMinted = pufferVault.deposit(1000 ether, alice); - - assertGt(aliceMinted, 0, "alice minted"); - - // Save total ETH backing before the rebase - uint256 backingETHAmountBefore = pufferVault.totalAssets(); - - // Check the balance before rebase - uint256 stethBalanceBefore = IERC20(stETH).balanceOf(address(pufferVault)); - - _rebaseLido(); - - assertTrue(pufferVault.totalAssets() > backingETHAmountBefore, "eth backing went down"); - - // Check the balance after rebase and assert that it increased - uint256 stethBalanceAfter = IERC20(stETH).balanceOf(address(pufferVault)); - - assertTrue(stethBalanceAfter > stethBalanceBefore, "lido rebase failed"); - - // After rebase, Bob is depositing 1k ETH - vm.startPrank(bob); - SafeERC20.safeIncreaseAllowance(IERC20(stETH), address(pufferVault), type(uint256).max); - uint256 bobMinted = pufferVault.deposit(1000 ether, bob); - - // Alice should have more pufferDepositor because the rebase happened after her deposit and changed the rate - assertTrue(aliceMinted > bobMinted, "alice should have more"); - - // ETH Backing after rebase should go up - assertTrue(pufferVault.totalAssets() > backingETHAmountBefore, "eth backing went down"); - } - - function test_depositingStETH_and_withdrawal() public { - test_minting_and_lido_rebasing(); - - // Check the balance of our vault - uint256 balance = stETH.balanceOf(address(address(pufferVault))); - - // We deposited 2k ETH, but because of the rebase we have more than 2k - // - uint256[] memory amounts = new uint256[](3); - amounts[0] = 1000 ether; // steth Amount - amounts[1] = 1000 ether; // steth Amount - amounts[2] = balance - 2000 ether; // the test - - uint256 assetsBefore = pufferVault.totalAssets(); - - // Initiate Withdrawals from lido - vm.startPrank(OPERATIONS_MULTISIG); - uint256[] memory requestIds = pufferVault.initiateETHWithdrawalsFromLido(amounts); - - assertApproxEqRel(assetsBefore, pufferVault.totalAssets(), 0.001e18, "bad accounting"); - - // Finalize them and fast forward to +10 days - _finalizeWithdrawals(requestIds[2]); - vm.roll(block.number + 10 days); // stupid bug - - // Claim withdrawals - pufferVault.claimWithdrawalsFromLido(requestIds); - - // Assert that we got more ETH than our original 2k ETH - assertGt(address(pufferVault).balance, 2000 ether, "oh no"); - } - - function test_deposit_wstETH_permit() - public - giveToken(0x0B925eD163218f6662a35e0f0371Ac234f9E9371, address(_WST_ETH), alice, 3000 ether) - withCaller(alice) - { - assertEq(0, pufferVault.balanceOf(alice), "alice has 0 pufETH"); - - Permit memory permit = _signPermit( - _testTemps( - "alice", - address(pufferDepositor), - 3000 ether, - block.timestamp, - hex"d4a8ff90a402dc7d4fcbf60f5488291263c743ccff180e139f47d139cedfd5fe" - ) - ); - - // Permit is good in this case - pufferDepositor.depositWstETH(permit); - - assertGt(pufferVault.balanceOf(alice), 0, "alice got pufETH"); - } - - function test_deposit_stETH_permit() - public - giveToken(BLAST_DEPOSIT, address(_ST_ETH), alice, 3000 ether) - withCaller(alice) - { - assertEq(0, pufferVault.balanceOf(alice), "alice has 0 pufETH"); - - Permit memory permit = _signPermit( - _testTemps( - "alice", - address(pufferDepositor), - 3000 ether, - block.timestamp, - hex"260e7e1a220ea89b9454cbcdc1fcc44087325df199a3986e560d75db18b2e253" - ) - ); - - // Permit is good in this case - pufferDepositor.depositStETH(permit); - - assertGt(pufferVault.balanceOf(alice), 0, "alice got pufETH"); - } - - function test_deposit_wstETH() - public - giveToken(0x0B925eD163218f6662a35e0f0371Ac234f9E9371, address(_WST_ETH), alice, 3000 ether) - withCaller(alice) - { - IERC20(address(_WST_ETH)).approve(address(pufferDepositor), type(uint256).max); - - assertEq(0, pufferVault.balanceOf(alice), "alice has 0 pufETH"); - - Permit memory permit = - _signPermit(_testTemps("alice", address(pufferDepositor), 3000 ether, block.timestamp, hex"")); - - // Permit call will revert because of the bad domain separator for wstETH - // But because we did the .approve, the transaction will succeed - pufferDepositor.depositWstETH(permit); - - assertGt(pufferVault.balanceOf(alice), 0, "alice got pufETH"); - } - - function _signPermit(_TestTemps memory t) internal pure returns (Permit memory p) { - bytes32 innerHash = keccak256(abi.encode(_PERMIT_TYPEHASH, t.owner, t.to, t.amount, t.nonce, t.deadline)); - bytes32 domainSeparator = t.domainSeparator; - bytes32 outerHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, innerHash)); - (t.v, t.r, t.s) = vm.sign(t.privateKey, outerHash); - - return Permit({ deadline: t.deadline, amount: t.amount, v: t.v, r: t.r, s: t.s }); - } - - function _testTemps(string memory seed, address to, uint256 amount, uint256 deadline, bytes32 domainSeparator) - internal - returns (_TestTemps memory t) - { - (t.owner, t.privateKey) = makeAddrAndKey(seed); - t.to = to; - t.amount = amount; - t.deadline = deadline; - t.domainSeparator = domainSeparator; - } -} diff --git a/mainnet-contracts/test/unit/PufETH.t.sol b/mainnet-contracts/test/unit/PufETH.t.sol index cdc49510..bf8f8c1d 100644 --- a/mainnet-contracts/test/unit/PufETH.t.sol +++ b/mainnet-contracts/test/unit/PufETH.t.sol @@ -62,10 +62,6 @@ contract PufETHTest is ERC4626Test { address msgSender = makeAddr("random"); vm.startPrank(msgSender); - uint256[] memory amounts = new uint256[](1); - vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, msgSender)); - pufferVault.initiateETHWithdrawalsFromLido(amounts); - vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, msgSender)); pufferVault.upgradeToAndCall(address(pufferDepositor), ""); } diff --git a/mainnet-contracts/test/unit/PufferVaultV2Property.t.sol b/mainnet-contracts/test/unit/PufferVaultV2Property.t.sol deleted file mode 100644 index c6d19c61..00000000 --- a/mainnet-contracts/test/unit/PufferVaultV2Property.t.sol +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import "erc4626-tests/ERC4626.test.sol"; -import { IStETH } from "../../src/interface/Lido/IStETH.sol"; -import { PufferDepositor } from "../../src/PufferDepositor.sol"; -import { PufferVault } from "../../src/PufferVault.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; -import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; -import { PufferDeployment } from "../../src/structs/PufferDeployment.sol"; -import { BridgingDeployment } from "script/DeploymentStructs.sol"; -import { DeployPufETHBridging } from "script/DeployPufETHBridging.s.sol"; - -import { DeployPufETH } from "script/DeployPufETH.s.sol"; -import { UpgradePufETH } from "script/UpgradePufETH.s.sol"; -import { MockPufferOracle } from "../mocks/MockPufferOracle.sol"; -import { WETH9 } from "../mocks/WETH9.sol"; -import { ROLE_ID_DAO } from "../../script/Roles.sol"; -import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; -import { MockRevenueDepositor } from "../mocks/MockRevenueDepositor.sol"; - -contract PufferVaultV2Property is ERC4626Test { - PufferDepositor public pufferDepositor; - PufferVaultV2 public pufferVault; - AccessManager public accessManager; - IStETH public stETH; - WETH9 public weth; - - // Override the minting of shares and assets (limit to uin64.max) - function setUpVault(Init memory init) public virtual override { - // setup initial shares and assets for individual users - for (uint256 i = 0; i < N; i++) { - address user = init.user[i]; - vm.assume(_isEOA(user)); - // shares - uint256 shares = init.share[i]; - vm.assume(shares < type(uint64).max); - try IMockERC20(_underlying_).mint(user, shares) { } - catch { - vm.assume(false); - } - _approve(_underlying_, user, _vault_, shares); - vm.prank(user); - try IERC4626(_vault_).deposit(shares, user) { } - catch { - vm.assume(false); - } - // assets - uint256 assets = init.asset[i]; - vm.assume(assets < type(uint64).max); - try IMockERC20(_underlying_).mint(user, assets) { } - catch { - vm.assume(false); - } - } - - // setup initial yield for vault - setUpYield(init); - } - - function setUp() public override { - PufferDeployment memory deployment = new DeployPufETH().run(); - - MockPufferOracle mockOracle = new MockPufferOracle(); - MockRevenueDepositor mockRevenueDepositor = new MockRevenueDepositor(); - - BridgingDeployment memory bridgingDeployment = new DeployPufETHBridging().run(deployment); - - new UpgradePufETH().run(deployment, bridgingDeployment, address(mockOracle), address(mockRevenueDepositor)); - - pufferDepositor = PufferDepositor(payable(deployment.pufferDepositor)); - pufferVault = PufferVaultV2(payable(deployment.pufferVault)); - accessManager = AccessManager(payable(deployment.accessManager)); - stETH = IStETH(payable(deployment.stETH)); - - // vm.prank(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); - // accessManager.grantRole(ROLE_ID_DAO, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 0); - // vm.prank(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); - // pufferVault.setDailyWithdrawalLimit(type(uint96).max); - - // Setup access for public - bytes memory encodedMulticall = - new GenerateAccessManagerCallData().run(address(pufferVault), address(pufferDepositor)); - - vm.prank(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); - (bool s,) = address(accessManager).call(encodedMulticall); - require(s, "success"); - - weth = WETH9(payable(deployment.weth)); - - _underlying_ = address(deployment.weth); - _vault_ = address(pufferVault); - _delta_ = 0; - _vaultMayBeEmpty = false; - _unlimitedAmount = false; - } - - // In test/Integration/PufferVaultV2.fork.t.sol we test that ETH and WETH and STETH deposits should give you the same amount of shares - // in `test_eth_weth_stETH_deposits` -} diff --git a/mainnet-contracts/test/unit/Timelock.t.sol b/mainnet-contracts/test/unit/Timelock.t.sol index 77d22e1c..de7c4d38 100644 --- a/mainnet-contracts/test/unit/Timelock.t.sol +++ b/mainnet-contracts/test/unit/Timelock.t.sol @@ -34,12 +34,8 @@ contract TimelockTest is Test { vm.assume(caller != address(timelock)); vm.assume(caller != address(accessManager)); - (bool canCall, uint32 delay) = - accessManager.canCall(caller, address(pufferVault), PufferVault.initiateETHWithdrawalsFromLido.selector); - assertFalse(canCall, "should not be able to call"); - // Upgrades are forbidden - (canCall, delay) = + (bool canCall, uint32 delay) = accessManager.canCall(caller, address(pufferVault), UUPSUpgradeable.upgradeToAndCall.selector); assertFalse(canCall, "should not be able to call"); From 401ce325e4e6c6c4f60846ae443f822b4408546f Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 13:24:04 +0100 Subject: [PATCH 18/37] move everything to vault v5 --- .../script/DeployPufETHBridging.s.sol | 2 +- mainnet-contracts/script/UpgradePufETH.s.sol | 29 +- mainnet-contracts/src/PufferVaultStorage.sol | 2 +- mainnet-contracts/src/PufferVaultV5.sol | 569 ++++++++++++++++++ .../test/Integration/PufferVaultV3.fork.t.sol | 61 -- .../test/MainnetForkTestHelper.sol | 37 +- ...aultV4Tests.sol => PufferVaultV5Tests.sol} | 10 +- mainnet-contracts/test/unit/PufETH.t.sol | 1 - .../test/unit/PufferWithdrawalManager.t.sol | 1 + 9 files changed, 613 insertions(+), 99 deletions(-) create mode 100644 mainnet-contracts/src/PufferVaultV5.sol delete mode 100644 mainnet-contracts/test/Integration/PufferVaultV3.fork.t.sol rename mainnet-contracts/test/mocks/{PufferVaultV4Tests.sol => PufferVaultV5Tests.sol} (72%) diff --git a/mainnet-contracts/script/DeployPufETHBridging.s.sol b/mainnet-contracts/script/DeployPufETHBridging.s.sol index 32ce9479..85112410 100644 --- a/mainnet-contracts/script/DeployPufETHBridging.s.sol +++ b/mainnet-contracts/script/DeployPufETHBridging.s.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Script.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { BaseScript } from ".//BaseScript.s.sol"; -import { PufferVault } from "../src/PufferVault.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { PufferDeployment } from "../src/structs/PufferDeployment.sol"; import { BridgingDeployment } from "./DeploymentStructs.sol"; diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index 204b214f..adfe4b5d 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -3,25 +3,24 @@ pragma solidity >=0.8.0 <0.9.0; import { stdJson } from "forge-std/StdJson.sol"; import { BaseScript } from ".//BaseScript.s.sol"; -import { PufferVault } from "../src/PufferVault.sol"; -import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; -import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; +import { PufferVaultV5Tests } from "../test/mocks/PufferVaultV5Tests.sol"; import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; import { LidoWithdrawalQueueMock } from "../test/mocks/LidoWithdrawalQueueMock.sol"; -import { stETHStrategyMock } from "../test/mocks/stETHStrategyMock.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { IWETH } from "../src/interface/Other/IWETH.sol"; -import { IPufferOracle } from "../src/interface/IPufferOracle.sol"; +import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { PufferDeployment } from "../src/structs/PufferDeployment.sol"; import { BridgingDeployment } from "./DeploymentStructs.sol"; import { IPufferRevenueDepositor } from "../src/interface/IPufferRevenueDepositor.sol"; +import { IPufferOracle } from "../src/interface/IPufferOracle.sol"; /** * @title UpgradePufETH @@ -61,18 +60,26 @@ contract UpgradePufETH is BaseScript { //@todo this is for tests only AccessManager(deployment.accessManager).grantRole(1, _broadcaster, 0); - PufferVaultV4Tests newImplementation = new PufferVaultV4Tests( + PufferVaultV2 newImplementationV2 = new PufferVaultV2( IStETH(deployment.stETH), IWETH(deployment.weth), ILidoWithdrawalQueue(deployment.lidoWithdrawalQueueMock), - IPufferOracle(pufferOracle), - IPufferRevenueDepositor(revenueDepositor) + IPufferOracle(pufferOracle) ); - vm.expectEmit(true, true, true, true); - emit Initializable.Initialized(2); + // Initialize VaultV2 to swap stETH for WETH as the asset UUPSUpgradeable(deployment.pufferVault).upgradeToAndCall( - address(newImplementation), abi.encodeCall(PufferVaultV2.initialize, ()) + address(newImplementationV2), abi.encodeCall(PufferVaultV2.initialize, ()) + ); + + PufferVaultV5 newImplementation = new PufferVaultV5Tests( + IStETH(deployment.stETH), + IWETH(deployment.weth), + ILidoWithdrawalQueue(deployment.lidoWithdrawalQueueMock), + IPufferOracleV2(pufferOracle), + IPufferRevenueDepositor(revenueDepositor) ); + + UUPSUpgradeable(deployment.pufferVault).upgradeToAndCall(address(newImplementation), ""); } } diff --git a/mainnet-contracts/src/PufferVaultStorage.sol b/mainnet-contracts/src/PufferVaultStorage.sol index c4635500..5066976e 100644 --- a/mainnet-contracts/src/PufferVaultStorage.sol +++ b/mainnet-contracts/src/PufferVaultStorage.sol @@ -23,7 +23,7 @@ abstract contract PufferVaultStorage { uint256 lidoLockedETH; uint256 deprecated_eigenLayerPendingWithdrawalSharesAmount; // Not in use anymore bool deprecated_isLidoWithdrawal; // Not in use in PufferVaultV2 - EnumerableSet.UintSet lidoWithdrawals; // Not in use in PufferVaultV2 + EnumerableSet.UintSet deprecated_lidoWithdrawals; // Not in use in PufferVaultV2 EnumerableSet.Bytes32Set deprecated_eigenLayerWithdrawals; // Not in use anymore EnumerableMap.UintToUintMap lidoWithdrawalAmounts; // 1 Slot for daily withdrawal limits diff --git a/mainnet-contracts/src/PufferVaultV5.sol b/mainnet-contracts/src/PufferVaultV5.sol new file mode 100644 index 00000000..1b85629d --- /dev/null +++ b/mainnet-contracts/src/PufferVaultV5.sol @@ -0,0 +1,569 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import { PufferVaultStorage } from "./PufferVaultStorage.sol"; +import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import { AccessManagedUpgradeable } from + "@openzeppelin-contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; +import { ERC4626Upgradeable } from "@openzeppelin-contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; +import { ERC20Upgradeable } from "@openzeppelin-contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import { ERC20PermitUpgradeable } from + "@openzeppelin-contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; +import { IStETH } from "./interface/Lido/IStETH.sol"; +import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; +import { IWETH } from "./interface/Other/IWETH.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; +import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IPufferOracleV2 } from "./interface/IPufferOracleV2.sol"; +import { IPufferVaultV2 } from "./interface/IPufferVaultV2.sol"; +import { IPufferRevenueDepositor } from "./interface/IPufferRevenueDepositor.sol"; + +/** + * @title PufferVaultV5 + * @dev Implementation of the PufferVault version 5 contract. + * @custom:security-contact security@puffer.fi + */ +contract PufferVaultV5 is + IPufferVaultV3, + IERC721Receiver, + PufferVaultStorage, + AccessManagedUpgradeable, + ERC20PermitUpgradeable, + ERC4626Upgradeable, + UUPSUpgradeable +{ + using SafeERC20 for address; + using EnumerableMap for EnumerableMap.UintToUintMap; + using EnumerableSet for EnumerableSet.Bytes32Set; + using Math for uint256; + + uint256 private constant _BASIS_POINT_SCALE = 1e4; + + /** + * @dev stETH contract + */ + IStETH internal immutable _ST_ETH; + + /** + * @dev Lido Withdrawal Queue + */ + ILidoWithdrawalQueue internal immutable _LIDO_WITHDRAWAL_QUEUE; + + /** + * @dev The Wrapped Ethereum ERC20 token + */ + IWETH internal immutable _WETH; + + /** + * @dev The PufferOracle contract + */ + IPufferOracleV2 public immutable PUFFER_ORACLE; + + /** + * @notice The restaking rewards depositor contract. + */ + IPufferRevenueDepositor public immutable RESTAKING_REWARDS_DEPOSITOR; + + constructor( + IStETH stETH, + ILidoWithdrawalQueue lidoWithdrawalQueue, + IWETH weth, + IPufferOracleV2 pufferOracle, + IPufferRevenueDepositor revenueDepositor + ) { + _ST_ETH = stETH; + _LIDO_WITHDRAWAL_QUEUE = lidoWithdrawalQueue; + _WETH = weth; + PUFFER_ORACLE = pufferOracle; + RESTAKING_REWARDS_DEPOSITOR = revenueDepositor; + _disableInitializers(); + } + + receive() external payable virtual { } + + /** + * @inheritdoc IPufferVaultV3 + */ + function getTotalRewardMintAmount() public view returns (uint256) { + VaultStorage storage $ = _getPufferVaultStorage(); + return $.totalRewardMintAmount; + } + + /** + * @inheritdoc IPufferVaultV3 + */ + function getTotalRewardDepositAmount() public view returns (uint256) { + VaultStorage storage $ = _getPufferVaultStorage(); + return $.totalRewardDepositAmount; + } + + /** + * @notice Mints pufETH rewards for the L1RewardManager contract and returns the exchange rate. + * @dev Restricted to L1RewardManager + */ + function mintRewards(uint256 rewardsAmount) + external + restricted + returns (uint256 ethToPufETHRate, uint256 pufETHAmount) + { + ethToPufETHRate = convertToShares(1 ether); + // calculate the shares using this formula since calling convertToShares again is costly + pufETHAmount = ethToPufETHRate.mulDiv(rewardsAmount, 1 ether, Math.Rounding.Floor); + + VaultStorage storage $ = _getPufferVaultStorage(); + + uint256 previousRewardsAmount = $.totalRewardMintAmount; + uint256 newTotalRewardsAmount = previousRewardsAmount + rewardsAmount; + $.totalRewardMintAmount = newTotalRewardsAmount; + + emit UpdatedTotalRewardsAmount(previousRewardsAmount, newTotalRewardsAmount, 0); + + // msg.sender is the L1RewardManager contract + _mint(msg.sender, pufETHAmount); + + return (ethToPufETHRate, pufETHAmount); + } + + /** + * @notice Deposits the rewards amount to the vault and updates the total reward deposit amount. + * @dev Restricted to PufferModuleManager + */ + function depositRewards() external payable restricted { + VaultStorage storage $ = _getPufferVaultStorage(); + uint256 previousRewardsAmount = $.totalRewardDepositAmount; + uint256 newTotalRewardsAmount = previousRewardsAmount + msg.value; + $.totalRewardDepositAmount = newTotalRewardsAmount; + + emit UpdatedTotalRewardsAmount(previousRewardsAmount, newTotalRewardsAmount, msg.value); + } + + /** + * @notice Reverts the `mintRewards` action. + * @dev Restricted to L1RewardManager + */ + function revertMintRewards(uint256 pufETHAmount, uint256 ethAmount) external restricted { + VaultStorage storage $ = _getPufferVaultStorage(); + + uint256 previousMintAmount = $.totalRewardMintAmount; + // nosemgrep basic-arithmetic-underflow + uint256 newMintAmount = previousMintAmount - ethAmount; + $.totalRewardMintAmount = newMintAmount; + + emit UpdatedTotalRewardsAmount(previousMintAmount, newMintAmount, 0); + + // msg.sender is the L1RewardManager contract + _burn(msg.sender, pufETHAmount); + } + + /** + * @dev See {IERC4626-totalAssets}. + * pufETH, the shares of the vault, will be backed primarily by the WETH asset. + * However, at any point in time, the full backings may be a combination of stETH, WETH, and ETH. + * `totalAssets()` is calculated by summing the following: + * - WETH held in the vault contract + * - ETH held in the vault contract + * - PUFFER_ORACLE.getLockedEthAmount(), which is the oracle-reported Puffer validator ETH locked in the Beacon chain + * - getTotalRewardMintAmount(), which is the total amount of rewards minted + * - getTotalRewardDepositAmount(), which is the total amount of rewards deposited to the Vault + * - RESTAKING_REWARDS_DEPOSITOR.getPendingDistributionAmount(), which is the total amount of rewards pending distribution + * + * NOTE on the native ETH deposits: + * When dealing with NATIVE ETH deposits, we need to deduct callvalue from the balance. + * The contract calculates the amount of shares(pufETH) to mint based on the total assets. + * When a user sends ETH, the msg.value is immediately added to address(this).balance. + * Since address(this.balance)` is used in calculating `totalAssets()`, we must deduct the `callvalue()` from the balance to prevent the user from minting excess shares. + * `msg.value` cannot be accessed from a view function, so we use assembly to get the callvalue. + */ + function totalAssets() public view virtual override returns (uint256) { + uint256 callValue; + // solhint-disable-next-line no-inline-assembly + assembly { + callValue := callvalue() + } + return _ST_ETH.balanceOf(address(this)) + getPendingLidoETHAmount() + _WETH.balanceOf(address(this)) + + (address(this).balance - callValue) + PUFFER_ORACLE.getLockedEthAmount() + getTotalRewardMintAmount() + - getTotalRewardDepositAmount() - RESTAKING_REWARDS_DEPOSITOR.getPendingDistributionAmount(); + } + + /** + * @notice Withdrawals WETH assets from the vault, burning the `owner`'s (pufETH) shares. + * The caller of this function does not have to be the `owner` if the `owner` has approved the caller to spend their pufETH. + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + * Copied the original ERC4626 code back to override `PufferVault` + wrap ETH logic + * @param assets The amount of assets (WETH) to withdraw + * @param receiver The address to receive the assets (WETH) + * @param owner The address of the owner for which the shares (pufETH) are burned. + * @return shares The amount of shares (pufETH) burned + */ + function withdraw(uint256 assets, address receiver, address owner) + public + virtual + override + revertIfDeposited + restricted + returns (uint256) + { + uint256 maxAssets = maxWithdraw(owner); + if (assets > maxAssets) { + revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets); + } + + _wrapETH(assets); + + uint256 shares = previewWithdraw(assets); + _withdraw({ caller: _msgSender(), receiver: receiver, owner: owner, assets: assets, shares: shares }); + + return shares; + } + + /** + * @notice Redeems (pufETH) `shares` to receive (WETH) assets from the vault, burning the `owner`'s (pufETH) `shares`. + * The caller of this function does not have to be the `owner` if the `owner` has approved the caller to spend their pufETH. + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + * Copied the original ERC4626 code back to override `PufferVault` + wrap ETH logic + * @param shares The amount of shares (pufETH) to withdraw + * @param receiver The address to receive the assets (WETH) + * @param owner The address of the owner for which the shares (pufETH) are burned. + * @return assets The amount of assets (WETH) redeemed + */ + function redeem(uint256 shares, address receiver, address owner) + public + virtual + override + revertIfDeposited + restricted + returns (uint256) + { + uint256 maxShares = maxRedeem(owner); + if (shares > maxShares) { + revert ERC4626ExceededMaxRedeem(owner, shares, maxShares); + } + + uint256 assets = previewRedeem(shares); + + _wrapETH(assets); + + _withdraw({ caller: _msgSender(), receiver: receiver, owner: owner, assets: assets, shares: shares }); + + return assets; + } + + /** + * @inheritdoc IPufferVaultV2 + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + */ + function depositETH(address receiver) public payable virtual markDeposit restricted returns (uint256) { + uint256 maxAssets = maxDeposit(receiver); + if (msg.value > maxAssets) { + revert ERC4626ExceededMaxDeposit(receiver, msg.value, maxAssets); + } + + uint256 shares = previewDeposit(msg.value); + _mint(receiver, shares); + emit Deposit(_msgSender(), receiver, msg.value, shares); + + return shares; + } + + /** + * @inheritdoc IPufferVaultV2 + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + */ + function depositStETH(uint256 stETHSharesAmount, address receiver) + public + virtual + markDeposit + restricted + returns (uint256) + { + uint256 maxAssets = maxDeposit(receiver); + + // Get the amount of assets (stETH) that corresponds to `stETHSharesAmount` so that we can use it in our calculation + uint256 assets = _ST_ETH.getPooledEthByShares(stETHSharesAmount); + + if (assets > maxAssets) { + revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets); + } + + uint256 shares = previewDeposit(assets); + // Transfer the exact number of stETH shares from the user to the vault + _ST_ETH.transferSharesFrom({ _sender: msg.sender, _recipient: address(this), _sharesAmount: stETHSharesAmount }); + _mint(receiver, shares); + + emit Deposit(_msgSender(), receiver, assets, shares); + + return shares; + } + + /** + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + */ + function deposit(uint256 assets, address receiver) + public + virtual + override + markDeposit + restricted + returns (uint256) + { + return super.deposit(assets, receiver); + } + + /** + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + */ + function mint(uint256 shares, address receiver) public virtual override markDeposit restricted returns (uint256) { + return super.mint(shares, receiver); + } + + /** + * @notice Initiates ETH withdrawals from Lido + * @dev Restricted to Operations Multisig + * @param amounts An array of stETH amounts to queue + * @return requestIds An array of request IDs for the withdrawals + */ + function initiateETHWithdrawalsFromLido(uint256[] calldata amounts) + external + virtual + restricted + returns (uint256[] memory requestIds) + { + require(amounts.length != 0); + VaultStorage storage $ = _getPufferVaultStorage(); + + uint256 lockedAmount; + for (uint256 i = 0; i < amounts.length; ++i) { + lockedAmount += amounts[i]; + } + $.lidoLockedETH += lockedAmount; + + SafeERC20.safeIncreaseAllowance(_ST_ETH, address(_LIDO_WITHDRAWAL_QUEUE), lockedAmount); + requestIds = _LIDO_WITHDRAWAL_QUEUE.requestWithdrawals(amounts, address(this)); + + // nosemgrep array-length-outside-loop + for (uint256 i = 0; i < requestIds.length; ++i) { + $.lidoWithdrawalAmounts.set(requestIds[i], amounts[i]); + } + emit RequestedWithdrawals(requestIds); + return requestIds; + } + + /** + * @notice Claims ETH withdrawals from Lido + * @dev Restricted to Operations Multisig + * @param requestIds An array of request IDs for the withdrawals + */ + function claimWithdrawalsFromLido(uint256[] calldata requestIds) external virtual restricted { + require(requestIds.length != 0); + VaultStorage storage $ = _getPufferVaultStorage(); + + // ETH balance before the claim + uint256 balanceBefore = address(this).balance; + + uint256 expectedWithdrawal = 0; + + for (uint256 i = 0; i < requestIds.length; ++i) { + // .get reverts if requestId is not present + expectedWithdrawal += $.lidoWithdrawalAmounts.get(requestIds[i]); + $.lidoWithdrawalAmounts.remove(requestIds[i]); + + // slither-disable-next-line calls-loop + _LIDO_WITHDRAWAL_QUEUE.claimWithdrawal(requestIds[i]); + } + + // ETH balance after the claim + uint256 balanceAfter = address(this).balance; + uint256 actualWithdrawal = balanceAfter - balanceBefore; + // Deduct from the locked amount the expected amount + // nosemgrep basic-arithmetic-underflow + $.lidoLockedETH -= expectedWithdrawal; + + emit ClaimedWithdrawals(requestIds); + emit LidoWithdrawal(expectedWithdrawal, actualWithdrawal); + } + + /** + * @notice Transfers ETH to a specified address. + * @dev Restricted to PufferProtocol smart contract + * @dev It is used to transfer ETH to PufferModules to fund Puffer validators. + * @param to The address of the PufferModule to transfer ETH to + * @param ethAmount The amount of ETH to transfer + */ + function transferETH(address to, uint256 ethAmount) external restricted { + // Our Vault holds ETH & WETH + // If we don't have enough ETH for the transfer, unwrap WETH + uint256 ethBalance = address(this).balance; + if (ethBalance < ethAmount) { + // Reverts if no WETH to unwrap + // nosemgrep basic-arithmetic-underflow + _WETH.withdraw(ethAmount - ethBalance); + } + + // slither-disable-next-line arbitrary-send-eth + (bool success,) = to.call{ value: ethAmount }(""); + + if (!success) { + revert ETHTransferFailed(); + } + + emit TransferredETH(to, ethAmount); + } + + /** + * @notice Returns the amount of ETH that is pending withdrawal from Lido + * @return The amount of ETH pending withdrawal + */ + function getPendingLidoETHAmount() public view virtual returns (uint256) { + VaultStorage storage $ = _getPufferVaultStorage(); + return $.lidoLockedETH; + } + + /** + * @notice Required by the ERC721 Standard to receive Lido withdrawal NFT + */ + function onERC721Received(address, address, uint256, bytes calldata) external virtual returns (bytes4) { + return IERC721Receiver.onERC721Received.selector; + } + + /** + * @notice Returns the number of decimals used to get its user representation. + */ + function decimals() public pure override(ERC20Upgradeable, ERC4626Upgradeable) returns (uint8) { + return 18; + } + + /** + * @notice Wraps the vault's ETH balance to WETH. + * @dev Used to provide WETH liquidity + */ + function _wrapETH(uint256 assets) internal virtual { + uint256 wethBalance = _WETH.balanceOf(address(this)); + + if (wethBalance < assets) { + _WETH.deposit{ value: assets - wethBalance }(); + } + } + + /** + * @notice Allows the `msg.sender` to burn their (pufETH) shares + * @dev Restricted to PufferProtocol + * It is used to burn portions of Puffer validator bonds due to inactivity or slashing + * @param shares The amount of shares to burn + */ + function burn(uint256 shares) public restricted { + _burn(msg.sender, shares); + } + + /** + * @notice Returns the amount of shares (pufETH) for the `assets` amount rounded up + * @param assets The amount of assets + */ + function convertToSharesUp(uint256 assets) public view returns (uint256) { + return _convertToShares(assets, Math.Rounding.Ceil); + } + + /** + * @param newExitFeeBasisPoints is the new exit fee basis points + * @dev Restricted to the DAO + */ + function setExitFeeBasisPoints(uint256 newExitFeeBasisPoints) external restricted { + _setExitFeeBasisPoints(newExitFeeBasisPoints); + } + + /** + * @dev Preview adding an exit fee on withdraw. See {IERC4626-previewWithdraw}. + */ + function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { + uint256 fee = _feeOnRaw(assets, getExitFeeBasisPoints()); + return super.previewWithdraw(assets + fee); + } + + /** + * @dev Preview taking an exit fee on redeem. See {IERC4626-previewRedeem}. + */ + function previewRedeem(uint256 shares) public view virtual override returns (uint256) { + uint256 assets = super.previewRedeem(shares); + // nosemgrep basic-arithmetic-underflow + return assets - _feeOnTotal(assets, getExitFeeBasisPoints()); + } + + /** + * @inheritdoc IPufferVaultV2 + */ + function getExitFeeBasisPoints() public view virtual returns (uint256) { + VaultStorage storage $ = _getPufferVaultStorage(); + return $.exitFeeBasisPoints; + } + + /** + * @dev Calculates the fees that should be added to an amount `assets` that does not already include fees. + * Used in {IERC4626-withdraw}. + */ + function _feeOnRaw(uint256 assets, uint256 feeBasisPoints) internal pure virtual returns (uint256) { + return assets.mulDiv(feeBasisPoints, _BASIS_POINT_SCALE, Math.Rounding.Ceil); + } + + /** + * @dev Calculates the fee part of an amount `assets` that already includes fees. + * Used in {IERC4626-redeem}. + */ + function _feeOnTotal(uint256 assets, uint256 feeBasisPoints) internal pure virtual returns (uint256) { + return assets.mulDiv(feeBasisPoints, feeBasisPoints + _BASIS_POINT_SCALE, Math.Rounding.Ceil); + } + + /** + * @notice Updates the exit fee basis points + * @dev 200 Basis points = 2% is the maximum exit fee + */ + function _setExitFeeBasisPoints(uint256 newExitFeeBasisPoints) internal virtual { + VaultStorage storage $ = _getPufferVaultStorage(); + // 2% is the maximum exit fee + if (newExitFeeBasisPoints > 200) { + revert InvalidExitFeeBasisPoints(); + } + emit ExitFeeBasisPointsSet($.exitFeeBasisPoints, newExitFeeBasisPoints); + $.exitFeeBasisPoints = newExitFeeBasisPoints; + } + + modifier markDeposit() virtual { + //solhint-disable-next-line no-inline-assembly + assembly { + tstore(_DEPOSIT_TRACKER_LOCATION, 1) // Store `1` in the deposit tracker location + } + _; + } + + modifier revertIfDeposited() virtual { + //solhint-disable-next-line no-inline-assembly + assembly { + // If the deposit tracker location is set to `1`, revert with `DepositAndWithdrawalForbidden()` + if tload(_DEPOSIT_TRACKER_LOCATION) { + mstore(0x00, 0x39b79d11) // Store the error signature `0x39b79d11` for `error DepositAndWithdrawalForbidden()` in memory. + revert(0x1c, 0x04) // Revert by returning those 4 bytes. `revert DepositAndWithdrawalForbidden()` + } + } + _; + } + + /** + * @dev Authorizes an upgrade to a new implementation + * Restricted access + * @param newImplementation The address of the new implementation + */ + // slither-disable-next-line dead-code + function _authorizeUpgrade(address newImplementation) internal virtual override restricted { } + + function _getERC4626StorageInternal() private pure returns (ERC4626Storage storage $) { + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC4626")) - 1)) & ~bytes32(uint256(0xff)) + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := 0x0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00 + } + } +} diff --git a/mainnet-contracts/test/Integration/PufferVaultV3.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV3.fork.t.sol deleted file mode 100644 index 8e652e4e..00000000 --- a/mainnet-contracts/test/Integration/PufferVaultV3.fork.t.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { ERC4626Upgradeable } from "@openzeppelin-contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; -import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; -import { IPufferVaultV3 } from "../../src/interface/IPufferVaultV3.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { ROLE_ID_DAO, ROLE_ID_PUFFER_PROTOCOL } from "../../script/Roles.sol"; -import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; - -contract PufferVaultV4ForkTest is MainnetForkTestHelper { - function setUp() public virtual override { - // Cancun upgrade - vm.createSelectFork(vm.rpcUrl("mainnet"), 21019835); // (Oct-22-2024 08:09:59 AM +UTC) - - // Setup contracts that are deployed to mainnet - _setupLiveContracts(); - - // Upgrade to latest version - _upgradeToMainnetV4Puffer(); - - // vm.startPrank(OPERATIONS_MULTISIG); - // xpufETH.setLockbox(address(lockBox)); - // xpufETH.setLimits(address(connext), 1000 ether, 1000 ether); - // pufferVault.setAllowedRewardMintFrequency(1 days); - // IPufferVaultV3.BridgeData memory bridgeData = IPufferVaultV3.BridgeData({ destinationDomainId: 1 }); - - // pufferVault.updateBridgeData(address(connext), bridgeData); - } - - // Sanity check - // function test_sanity() public view { - // assertEq(pufferVault.name(), "pufETH", "name"); - // assertEq(pufferVault.symbol(), "pufETH", "symbol"); - // assertEq(pufferVault.decimals(), 18, "decimals"); - // assertEq(pufferVault.asset(), address(_WETH), "asset"); - // assertEq(pufferVault.getTotalRewardMintAmount(), 0, "0 rewards"); - // } - - // function test_mintAndBridge() public { - // // first updateBridgeData - - // // IPufferVaultV3.MintAndBridgeParams memory params = IPufferVaultV3.MintAndBridgeParams({ - // // bridge: address(connext), - // // rewardsAmount: 100 ether, - // // startEpoch: 1, - // // endEpoch: 2, - // // rewardsRoot: bytes32(0), - // // rewardsURI: "uri" - // // }); - - // uint256 initialTotalAssets = pufferVault.totalAssets(); - - // // vm.startPrank(DAO); - - // // pufferVault.setAllowedRewardMintAmount(100 ether); - // // pufferVault.mintAndBridgeRewards{ value: 1 ether }(params); - - // assertEq(pufferVault.totalAssets(), initialTotalAssets + 100 ether); - // } -} diff --git a/mainnet-contracts/test/MainnetForkTestHelper.sol b/mainnet-contracts/test/MainnetForkTestHelper.sol index ab7cbe97..0460d797 100644 --- a/mainnet-contracts/test/MainnetForkTestHelper.sol +++ b/mainnet-contracts/test/MainnetForkTestHelper.sol @@ -5,9 +5,8 @@ import { Test } from "forge-std/Test.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; -import { PufferVaultV4 } from "../src/PufferVaultV4.sol"; -import { PufferVaultV2Tests } from "../test/mocks/PufferVaultV2Tests.sol"; -import { PufferVaultV4Tests } from "../test/mocks/PufferVaultV4Tests.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; +import { PufferVaultV5Tests } from "../test/mocks/PufferVaultV5Tests.sol"; import { PufferDepositorV2 } from "../src/PufferDepositorV2.sol"; import { MockPufferOracle } from "./mocks/MockPufferOracle.sol"; import { IEigenLayer } from "../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; @@ -16,16 +15,14 @@ import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils import { stdStorage, StdStorage } from "forge-std/Test.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { IStETH } from "../src/interface/Lido/IStETH.sol"; -import { IPufferOracle } from "../src/interface/IPufferOracle.sol"; +import { IPufferOracleV2 } from "src/interface/IPufferOracleV2.sol"; import { IWstETH } from "../src/interface/Lido/IWstETH.sol"; import { ILidoWithdrawalQueue } from "../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { Timelock } from "../src/Timelock.sol"; import { IWETH } from "../src/interface/Other/IWETH.sol"; import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; import { Permit } from "../src/structs/Permit.sol"; import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { DeployerHelper } from "../script/DeployerHelper.s.sol"; import { IPufferRevenueDepositor } from "../src/interface/IPufferRevenueDepositor.sol"; @@ -57,9 +54,9 @@ contract MainnetForkTestHelper is Test, DeployerHelper { PufferDepositorV2 public pufferDepositor; PufferVaultV3 public pufferVault; - PufferVaultV2 public pufferVaultWithBlocking; + PufferVaultV5 public pufferVaultWithBlocking; // Non blocking version is required because of the foundry tests - PufferVaultV2 public pufferVaultNonBlocking; + PufferVaultV5 public pufferVaultNonBlocking; AccessManager public accessManager; Timelock public timelock; @@ -148,22 +145,24 @@ contract MainnetForkTestHelper is Test, DeployerHelper { // We use MockOracle + MockPufferProtocol to simulate the Puffer Protocol MockPufferOracle mockOracle = new MockPufferOracle(); - pufferVaultNonBlocking = new PufferVaultV2Tests({ + pufferVaultNonBlocking = new PufferVaultV5Tests({ stETH: IStETH(_getStETH()), - weth: IWETH(_getWETH()), lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - oracle: mockOracle + weth: IWETH(_getWETH()), + oracle: mockOracle, + revenueDepositor: IPufferRevenueDepositor(address(0)) }); // Simulate that our deployed oracle becomes active and starts posting results of Puffer staking // At this time, we stop accepting stETH, and we accept only native ETH - PufferVaultV2 newImplementation = pufferVaultNonBlocking; + PufferVaultV5 newImplementation = pufferVaultNonBlocking; - pufferVaultWithBlocking = new PufferVaultV2({ + pufferVaultWithBlocking = new PufferVaultV5({ stETH: IStETH(_getStETH()), - weth: IWETH(_getWETH()), lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - oracle: mockOracle + weth: IWETH(_getWETH()), + pufferOracle: IPufferOracleV2(address(mockOracle)), + revenueDepositor: IPufferRevenueDepositor(address(0)) }); // Community multisig can do thing instantly @@ -218,17 +217,17 @@ contract MainnetForkTestHelper is Test, DeployerHelper { } function _upgradeToMainnetV4Puffer() internal { - pufferVaultNonBlocking = new PufferVaultV4Tests({ + pufferVaultNonBlocking = new PufferVaultV5Tests({ stETH: IStETH(_getStETH()), - weth: IWETH(_getWETH()), lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - oracle: IPufferOracle(_getPufferOracle()), + weth: IWETH(_getWETH()), + oracle: IPufferOracleV2(_getPufferOracle()), revenueDepositor: IPufferRevenueDepositor(address(0)) }); // Simulate that our deployed oracle becomes active and starts posting results of Puffer staking // At this time, we stop accepting stETH, and we accept only native ETH - PufferVaultV4 newImplementation = PufferVaultV4(payable(address(pufferVaultNonBlocking))); + PufferVaultV5 newImplementation = PufferVaultV5(payable(address(pufferVaultNonBlocking))); vm.startPrank(address(timelock)); vm.expectEmit(true, true, true, true); diff --git a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV5Tests.sol similarity index 72% rename from mainnet-contracts/test/mocks/PufferVaultV4Tests.sol rename to mainnet-contracts/test/mocks/PufferVaultV5Tests.sol index 39f16238..1e60e908 100644 --- a/mainnet-contracts/test/mocks/PufferVaultV4Tests.sol +++ b/mainnet-contracts/test/mocks/PufferVaultV5Tests.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { PufferVaultV4 } from "src/PufferVaultV4.sol"; +import { PufferVaultV5 } from "src/PufferVaultV5.sol"; import { IStETH } from "src/interface/Lido/IStETH.sol"; import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; import { IWETH } from "src/interface/Other/IWETH.sol"; -import { IPufferOracle } from "src/interface/IPufferOracle.sol"; +import { IPufferOracleV2 } from "src/interface/IPufferOracleV2.sol"; import { IPufferRevenueDepositor } from "src/interface/IPufferRevenueDepositor.sol"; -contract PufferVaultV4Tests is PufferVaultV4 { +contract PufferVaultV5Tests is PufferVaultV5 { constructor( IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, - IPufferOracle oracle, + IPufferOracleV2 oracle, IPufferRevenueDepositor revenueDepositor - ) PufferVaultV4(stETH, weth, lidoWithdrawalQueue, oracle, revenueDepositor) { + ) PufferVaultV5(stETH, lidoWithdrawalQueue, weth, oracle, revenueDepositor) { _disableInitializers(); } diff --git a/mainnet-contracts/test/unit/PufETH.t.sol b/mainnet-contracts/test/unit/PufETH.t.sol index bf8f8c1d..f4285871 100644 --- a/mainnet-contracts/test/unit/PufETH.t.sol +++ b/mainnet-contracts/test/unit/PufETH.t.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.0 <0.9.0; import "erc4626-tests/ERC4626.test.sol"; import { IStETH } from "../../src/interface/Lido/IStETH.sol"; -import { IPufferVault } from "../../src/interface/IPufferVault.sol"; import { IAccessManaged } from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import { PufferDepositor } from "../../src/PufferDepositor.sol"; import { PufferVault } from "../../src/PufferVault.sol"; diff --git a/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol b/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol index f6bdc902..e660faba 100644 --- a/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol +++ b/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol @@ -695,6 +695,7 @@ contract PufferWithdrawalManagerTest is UnitTestHelper { } function test_funds_returning_edge_case() public { + assertEq(pufferVault.asset(), address(weth), "asset should be WETH"); assertEq(pufferVault.totalSupply(), 1000 ether, "totalSupply should be 1000 ETH"); vm.startPrank(address(timelock)); From 5f0b6f15483cf309ac68f66f54cd08e4063ae5b5 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 13:53:00 +0100 Subject: [PATCH 19/37] remove everything checkpoint --- .../GenerateAccessManagerCalldata3.s.sol | 8 +- mainnet-contracts/script/DeployPuffer.s.sol | 4 +- .../DeployPufferProtocolImplementation.s.sol | 4 +- .../script/DeployPufferVaultV3.s.sol | 45 -- .../DeployPufferWithdrawalManager.s.sol | 4 +- ...GenerateBLSKeysAndRegisterValidators.s.sol | 4 +- mainnet-contracts/src/L1RewardManager.sol | 6 +- .../src/L1RewardManagerUnsafe.sol | 10 +- mainnet-contracts/src/PufferModuleManager.sol | 4 +- mainnet-contracts/src/PufferProtocol.sol | 8 +- .../src/PufferRevenueDepositor.sol | 6 +- mainnet-contracts/src/PufferVaultV3.sol | 118 ---- mainnet-contracts/src/PufferVaultV4.sol | 54 -- mainnet-contracts/src/PufferVaultV5.sol | 1 - .../src/PufferWithdrawalManager.sol | 14 +- mainnet-contracts/src/ValidatorTicket.sol | 6 +- .../src/echidna/EchidnaPufferVaultV2.sol | 19 - mainnet-contracts/src/echidna/config.yaml | 5 - .../src/interface/IPufferProtocol.sol | 4 +- .../interface/IPufferWithdrawalManager.sol | 8 +- .../Integration/PufferDepositorV2.fork.t.sol | 260 -------- .../PufferRevenueDepositor.fork.t.sol | 180 ------ .../test/Integration/PufferVaultV2.fork.t.sol | 582 ------------------ .../PufferVaultV2Sandwich.fork.t.sol | 100 --- .../PufferVaultV2WithdrawFromEl.fork.t.sol | 75 --- .../test/MainnetForkTestHelper.sol | 5 +- .../PufferWithdrawalManager.fork.t.sol | 4 +- .../ValidatorTicketMainnetTest.fork.t.sol | 9 +- .../test/handlers/PufferProtocolHandler.sol | 6 +- .../test/helpers/UnitTestHelper.sol | 9 +- .../test/mocks/PufferProtocolMockUpgrade.sol | 4 +- .../test/mocks/PufferVaultV2Tests.sol | 23 - .../mocks/PufferWithdrawalManagerTests.sol | 4 +- .../test/unit/PufferWithdrawalManager.t.sol | 4 +- .../test/unit/ValidatorTicket.t.sol | 9 +- .../test/unit/ValidatorTicketPricer.t.sol | 5 +- 36 files changed, 66 insertions(+), 1545 deletions(-) delete mode 100644 mainnet-contracts/script/DeployPufferVaultV3.s.sol delete mode 100644 mainnet-contracts/src/PufferVaultV3.sol delete mode 100644 mainnet-contracts/src/PufferVaultV4.sol delete mode 100644 mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol delete mode 100644 mainnet-contracts/src/echidna/config.yaml delete mode 100644 mainnet-contracts/test/Integration/PufferDepositorV2.fork.t.sol delete mode 100644 mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol delete mode 100644 mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol delete mode 100644 mainnet-contracts/test/Integration/PufferVaultV2Sandwich.fork.t.sol delete mode 100644 mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol delete mode 100644 mainnet-contracts/test/mocks/PufferVaultV2Tests.sol diff --git a/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata3.s.sol b/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata3.s.sol index 8b2fbfa8..611be62b 100644 --- a/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata3.s.sol +++ b/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata3.s.sol @@ -6,7 +6,7 @@ import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessMana import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol"; import { console } from "forge-std/console.sol"; import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; -import { PufferVaultV3 } from "../../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; import { L1RewardManager } from "../../src/L1RewardManager.sol"; import { L2RewardManager } from "l2-contracts/src/L2RewardManager.sol"; import { @@ -65,8 +65,8 @@ contract GenerateAccessManagerCalldata3 is Script { ); bytes4[] memory vaultSelectors = new bytes4[](2); - vaultSelectors[0] = PufferVaultV3.mintRewards.selector; - vaultSelectors[1] = PufferVaultV3.revertMintRewards.selector; + vaultSelectors[0] = PufferVaultV5.mintRewards.selector; + vaultSelectors[1] = PufferVaultV5.revertMintRewards.selector; calldatas[4] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, pufferVaultProxy, vaultSelectors, ROLE_ID_L1_REWARD_MANAGER ); @@ -75,7 +75,7 @@ contract GenerateAccessManagerCalldata3 is Script { abi.encodeWithSelector(AccessManager.grantRole.selector, ROLE_ID_L1_REWARD_MANAGER, l1RewardManagerProxy, 0); bytes4[] memory pufferModuleManagerSelectors = new bytes4[](1); - pufferModuleManagerSelectors[0] = PufferVaultV3.depositRewards.selector; + pufferModuleManagerSelectors[0] = PufferVaultV5.depositRewards.selector; calldatas[6] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, diff --git a/mainnet-contracts/script/DeployPuffer.s.sol b/mainnet-contracts/script/DeployPuffer.s.sol index a98c46a6..8b041181 100644 --- a/mainnet-contracts/script/DeployPuffer.s.sol +++ b/mainnet-contracts/script/DeployPuffer.s.sol @@ -16,7 +16,7 @@ import { BeaconMock } from "../test/mocks/BeaconMock.sol"; import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; -import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { GuardiansDeployment, PufferProtocolDeployment } from "./DeploymentStructs.sol"; import { ValidatorTicket } from "../src/ValidatorTicket.sol"; @@ -148,7 +148,7 @@ contract DeployPuffer is BaseScript { // Puffer Service implementation pufferProtocolImpl = new PufferProtocol({ - pufferVault: PufferVaultV2(payable(pufferVault)), + pufferVault: PufferVaultV5(payable(pufferVault)), validatorTicket: ValidatorTicket(address(validatorTicketProxy)), guardianModule: GuardianModule(payable(guardiansDeployment.guardianModule)), moduleManager: address(moduleManagerProxy), diff --git a/mainnet-contracts/script/DeployPufferProtocolImplementation.s.sol b/mainnet-contracts/script/DeployPufferProtocolImplementation.s.sol index 7459b524..d7fba15d 100644 --- a/mainnet-contracts/script/DeployPufferProtocolImplementation.s.sol +++ b/mainnet-contracts/script/DeployPufferProtocolImplementation.s.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Script.sol"; -import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { BaseScript } from "script/BaseScript.s.sol"; import { GuardianModule } from "../src/GuardianModule.sol"; import { PufferProtocol } from "../src/PufferProtocol.sol"; @@ -24,7 +24,7 @@ contract DeployPufferProtocolImplementation is DeployerHelper { address protocolImplementation = address( new PufferProtocol({ - pufferVault: PufferVaultV2(payable(_getPufferVault())), + pufferVault: PufferVaultV5(payable(_getPufferVault())), validatorTicket: ValidatorTicket(address(_getValidatorTicket())), guardianModule: GuardianModule(payable(_getGuardianModule())), moduleManager: _getPufferModuleManager(), diff --git a/mainnet-contracts/script/DeployPufferVaultV3.s.sol b/mainnet-contracts/script/DeployPufferVaultV3.s.sol deleted file mode 100644 index 8d260e0c..00000000 --- a/mainnet-contracts/script/DeployPufferVaultV3.s.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import "forge-std/Script.sol"; -import { stdJson } from "forge-std/StdJson.sol"; -import { DeployerHelper } from "./DeployerHelper.s.sol"; -import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; -import { PufferVaultV3 } from "src/PufferVaultV3.sol"; -import { IStETH } from "src/interface/Lido/IStETH.sol"; -import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IWETH } from "src/interface/Other/IWETH.sol"; -import { IPufferOracle } from "src/interface/IPufferOracle.sol"; - -/** - * @title DeployPufferVaultV3 - * @dev - * - * use either --account (keystore) or --private-key (env) - * - * forge script ./script/DeployPufferVaultV3.s.sol:DeployPufferVaultV3 --force --rpc-url $RPC_URL \ - * --verify \ - * --verifier-url if deploying on tenderly \ - * --etherscan-api-key $TENDERLY_ACCESS_KEY or $ETHERSCAN_API_KEY \ - * --broadcast - */ -contract DeployPufferVaultV3 is DeployerHelper { - function run() public { - vm.startBroadcast(); - - PufferVaultV3 pufferVaultV3Implementation = new PufferVaultV3({ - stETH: IStETH(_getStETH()), - weth: IWETH(_getWETH()), - lidoWithdrawalQueue: ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - oracle: IPufferOracle(_getPufferOracle()) - }); - - //@todo Double check reinitialization - _consoleLogOrUpgradeUUPS({ - proxyTarget: _getPufferVault(), - implementation: address(pufferVaultV3Implementation), - data: "", - contractName: "PufferVaultV3Implementation" - }); - } -} diff --git a/mainnet-contracts/script/DeployPufferWithdrawalManager.s.sol b/mainnet-contracts/script/DeployPufferWithdrawalManager.s.sol index 02edd57c..5f6418b5 100644 --- a/mainnet-contracts/script/DeployPufferWithdrawalManager.s.sol +++ b/mainnet-contracts/script/DeployPufferWithdrawalManager.s.sol @@ -6,7 +6,7 @@ import { DeployerHelper } from "./DeployerHelper.s.sol"; import { PufferWithdrawalManager } from "../src/PufferWithdrawalManager.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import { IWETH } from "../src/interface/Other/IWETH.sol"; -import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { Generate2StepWithdrawalsCalldata } from "./AccessManagerMigrations/04_Generate2StepWithdrawalsCalldata.s.sol"; /** @@ -24,7 +24,7 @@ contract DeployPufferWithdrawalManager is DeployerHelper { vm.startBroadcast(); PufferWithdrawalManager withdrawalManagerImpl = - ((new PufferWithdrawalManager(BATCH_SIZE, PufferVaultV3(payable(_getPufferVault())), IWETH(_getWETH())))); + ((new PufferWithdrawalManager(BATCH_SIZE, PufferVaultV5(payable(_getPufferVault())), IWETH(_getWETH())))); withdrawalManager = PufferWithdrawalManager( ( diff --git a/mainnet-contracts/script/GenerateBLSKeysAndRegisterValidators.s.sol b/mainnet-contracts/script/GenerateBLSKeysAndRegisterValidators.s.sol index d333d3f7..649ac165 100644 --- a/mainnet-contracts/script/GenerateBLSKeysAndRegisterValidators.s.sol +++ b/mainnet-contracts/script/GenerateBLSKeysAndRegisterValidators.s.sol @@ -7,7 +7,7 @@ import { Permit } from "../src/structs/Permit.sol"; import { ValidatorKeyData } from "../src/struct/ValidatorKeyData.sol"; import { IPufferProtocol } from "../src/interface/IPufferProtocol.sol"; import { PufferProtocol } from "../src/PufferProtocol.sol"; -import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { ValidatorTicket } from "../src/ValidatorTicket.sol"; /** @@ -21,7 +21,7 @@ import { ValidatorTicket } from "../src/ValidatorTicket.sol"; * To broadcast the transaction on-chain, add `--broadcast --slow` flag at the end of the command */ contract GenerateBLSKeysAndRegisterValidators is Script { - PufferVaultV2 internal pufETH; + PufferVaultV5 internal pufETH; ValidatorTicket internal validatorTicket; address internal protocolAddress; PufferProtocol internal pufferProtocol; diff --git a/mainnet-contracts/src/L1RewardManager.sol b/mainnet-contracts/src/L1RewardManager.sol index ab57d7dd..d4597cad 100644 --- a/mainnet-contracts/src/L1RewardManager.sol +++ b/mainnet-contracts/src/L1RewardManager.sol @@ -6,7 +6,7 @@ import { AccessManagedUpgradeable } from import { IXReceiver } from "@connext/interfaces/core/IXReceiver.sol"; import { IXERC20Lockbox } from "./interface/IXERC20Lockbox.sol"; import { IL1RewardManager } from "./interface/IL1RewardManager.sol"; -import { PufferVaultV3 } from "./PufferVaultV3.sol"; +import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { Unauthorized } from "mainnet-contracts/src/Errors.sol"; @@ -35,7 +35,7 @@ contract L1RewardManager is * @notice The PufferVault contract on Ethereum Mainnet * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ - PufferVaultV3 public immutable PUFFER_VAULT; + PufferVaultV5 public immutable PUFFER_VAULT; /** * @notice The XERC20Lockbox contract on Ethereum Mainnet * @custom:oz-upgrades-unsafe-allow state-variable-immutable @@ -53,7 +53,7 @@ contract L1RewardManager is constructor(address xPufETH, address lockbox, address pufETH, address l2RewardsManager) { XPUFETH = IERC20(xPufETH); LOCKBOX = IXERC20Lockbox(lockbox); - PUFFER_VAULT = PufferVaultV3(payable(pufETH)); + PUFFER_VAULT = PufferVaultV5(payable(pufETH)); L2_REWARDS_MANAGER = l2RewardsManager; _disableInitializers(); } diff --git a/mainnet-contracts/src/L1RewardManagerUnsafe.sol b/mainnet-contracts/src/L1RewardManagerUnsafe.sol index 1ad6abbd..1cf3c69a 100644 --- a/mainnet-contracts/src/L1RewardManagerUnsafe.sol +++ b/mainnet-contracts/src/L1RewardManagerUnsafe.sol @@ -6,7 +6,7 @@ import { AccessManagedUpgradeable } from import { IXReceiver } from "@connext/interfaces/core/IXReceiver.sol"; import { IXERC20Lockbox } from "./interface/IXERC20Lockbox.sol"; import { IL1RewardManager } from "./interface/IL1RewardManager.sol"; -import { PufferVaultV3 } from "./PufferVaultV3.sol"; +import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { Unauthorized } from "mainnet-contracts/src/Errors.sol"; @@ -98,22 +98,18 @@ contract L1RewardManagerUnsafe is { /** * @notice The XPUFETH token contract on Ethereum Mainnet - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ IERC20 public immutable XPUFETH; /** * @notice The PufferVault contract on Ethereum Mainnet - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ - PufferVaultV3 public immutable PUFFER_VAULT; + PufferVaultV5 public immutable PUFFER_VAULT; /** * @notice The XERC20Lockbox contract on Ethereum Mainnet - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ IXERC20Lockbox public immutable LOCKBOX; /** * @notice The Rewards Manager contract on L2 - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ address public immutable L2_REWARDS_MANAGER; @@ -123,7 +119,7 @@ contract L1RewardManagerUnsafe is constructor(address xPufETH, address lockbox, address pufETH, address l2RewardsManager) { XPUFETH = IERC20(xPufETH); LOCKBOX = IXERC20Lockbox(lockbox); - PUFFER_VAULT = PufferVaultV3(payable(pufETH)); + PUFFER_VAULT = PufferVaultV5(payable(pufETH)); L2_REWARDS_MANAGER = l2RewardsManager; _disableInitializers(); } diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index c71bcc4c..7dd78196 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -6,7 +6,7 @@ import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { Unauthorized, InvalidAmount } from "./Errors.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { PufferModule } from "./PufferModule.sol"; -import { PufferVaultV3 } from "./PufferVaultV3.sol"; +import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { RestakingOperator } from "./RestakingOperator.sol"; import { IPufferModuleManager } from "./interface/IPufferModuleManager.sol"; import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; @@ -127,7 +127,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, totalRewardsAmount += rewardsAmounts[i]; } - PufferVaultV3(PUFFER_VAULT).depositRewards{ value: totalRewardsAmount }(); + PufferVaultV5(PUFFER_VAULT).depositRewards{ value: totalRewardsAmount }(); } /** diff --git a/mainnet-contracts/src/PufferProtocol.sol b/mainnet-contracts/src/PufferProtocol.sol index 7da96911..fe8508ac 100644 --- a/mainnet-contracts/src/PufferProtocol.sol +++ b/mainnet-contracts/src/PufferProtocol.sol @@ -19,7 +19,7 @@ import { ProtocolStorage, NodeInfo, ModuleLimit } from "./struct/ProtocolStorage import { LibBeaconchainContract } from "./LibBeaconchainContract.sol"; import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import { PufferVaultV2 } from "./PufferVaultV2.sol"; +import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { ValidatorTicket } from "./ValidatorTicket.sol"; import { InvalidAddress } from "./Errors.sol"; import { StoppedValidatorInfo } from "./struct/StoppedValidatorInfo.sol"; @@ -83,7 +83,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad /** * @inheritdoc IPufferProtocol */ - PufferVaultV2 public immutable override PUFFER_VAULT; + PufferVaultV5 public immutable override PUFFER_VAULT; /** * @inheritdoc IPufferProtocol @@ -101,7 +101,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad IBeaconDepositContract public immutable override BEACON_DEPOSIT_CONTRACT; constructor( - PufferVaultV2 pufferVault, + PufferVaultV5 pufferVault, IGuardianModule guardianModule, address moduleManager, ValidatorTicket validatorTicket, @@ -109,7 +109,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad address beaconDepositContract ) { GUARDIAN_MODULE = guardianModule; - PUFFER_VAULT = PufferVaultV2(payable(address(pufferVault))); + PUFFER_VAULT = PufferVaultV5(payable(address(pufferVault))); PUFFER_MODULE_MANAGER = PufferModuleManager(payable(moduleManager)); VALIDATOR_TICKET = validatorTicket; PUFFER_ORACLE = oracle; diff --git a/mainnet-contracts/src/PufferRevenueDepositor.sol b/mainnet-contracts/src/PufferRevenueDepositor.sol index 12de2aee..235f2077 100644 --- a/mainnet-contracts/src/PufferRevenueDepositor.sol +++ b/mainnet-contracts/src/PufferRevenueDepositor.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { PufferVaultV4 } from "./PufferVaultV4.sol"; +import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { IWETH } from "./interface/Other/IWETH.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; @@ -37,7 +37,7 @@ contract PufferRevenueDepositor is * @notice PufferVault contract. * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ - PufferVaultV4 public immutable PUFFER_VAULT; + PufferVaultV5 public immutable PUFFER_VAULT; /** * @notice AeraVault contract. @@ -59,7 +59,7 @@ contract PufferRevenueDepositor is if (vault == address(0) || weth == address(0) || aeraVault == address(0)) { revert InvalidAddress(); } - PUFFER_VAULT = PufferVaultV4(payable(vault)); + PUFFER_VAULT = PufferVaultV5(payable(vault)); AERA_VAULT = IAeraVault(aeraVault); WETH = IWETH(weth); _disableInitializers(); diff --git a/mainnet-contracts/src/PufferVaultV3.sol b/mainnet-contracts/src/PufferVaultV3.sol deleted file mode 100644 index 895e9e80..00000000 --- a/mainnet-contracts/src/PufferVaultV3.sol +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { PufferVaultV2 } from "./PufferVaultV2.sol"; -import { IStETH } from "./interface/Lido/IStETH.sol"; -import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IWETH } from "./interface/Other/IWETH.sol"; -import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; -import { IPufferOracle } from "./interface/IPufferOracle.sol"; -import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; - -/** - * @title PufferVaultV3 - * @dev Implementation of the PufferVault version 3 contract. - * @notice This contract extends the functionality of PufferVaultV2 with additional features for reward minting and bridging. - * @custom:security-contact security@puffer.fi - */ -contract PufferVaultV3 is PufferVaultV2, IPufferVaultV3 { - using Math for uint256; - - /** - * @notice Initializes the PufferVaultV3 contract. - * @param stETH Address of the stETH token contract. - * @param weth Address of the WETH token contract. - * @param lidoWithdrawalQueue Address of the Lido withdrawal queue contract. - * @param oracle Address of the PufferOracle contract. - * @custom:oz-upgrades-unsafe-allow constructor - */ - constructor(IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, IPufferOracle oracle) - PufferVaultV2(stETH, weth, lidoWithdrawalQueue, oracle) - { - _disableInitializers(); - } - - /** - * @notice Returns the total assets held by the vault. - * @dev Returns the total assets held by the vault, including ETH held in the eigenpods as a result of receiving rewards. - * See {PufferVaultV2-totalAssets}. for more information. - * @return The total assets held by the vault. - */ - function totalAssets() public view virtual override returns (uint256) { - return (super.totalAssets() + getTotalRewardMintAmount() - getTotalRewardDepositAmount()); - } - - /** - * @inheritdoc IPufferVaultV3 - */ - function getTotalRewardMintAmount() public view returns (uint256) { - VaultStorage storage $ = _getPufferVaultStorage(); - return $.totalRewardMintAmount; - } - - /** - * @inheritdoc IPufferVaultV3 - */ - function getTotalRewardDepositAmount() public view returns (uint256) { - VaultStorage storage $ = _getPufferVaultStorage(); - return $.totalRewardDepositAmount; - } - - /** - * @notice Mints pufETH rewards for the L1RewardManager contract and returns the exchange rate. - * @dev Restricted to L1RewardManager - */ - function mintRewards(uint256 rewardsAmount) - external - restricted - returns (uint256 ethToPufETHRate, uint256 pufETHAmount) - { - ethToPufETHRate = convertToShares(1 ether); - // calculate the shares using this formula since calling convertToShares again is costly - pufETHAmount = ethToPufETHRate.mulDiv(rewardsAmount, 1 ether, Math.Rounding.Floor); - - VaultStorage storage $ = _getPufferVaultStorage(); - - uint256 previousRewardsAmount = $.totalRewardMintAmount; - uint256 newTotalRewardsAmount = previousRewardsAmount + rewardsAmount; - $.totalRewardMintAmount = newTotalRewardsAmount; - - emit UpdatedTotalRewardsAmount(previousRewardsAmount, newTotalRewardsAmount, 0); - - // msg.sender is the L1RewardManager contract - _mint(msg.sender, pufETHAmount); - - return (ethToPufETHRate, pufETHAmount); - } - - /** - * @notice Deposits the rewards amount to the vault and updates the total reward deposit amount. - * @dev Restricted to PufferModuleManager - */ - function depositRewards() external payable restricted { - VaultStorage storage $ = _getPufferVaultStorage(); - uint256 previousRewardsAmount = $.totalRewardDepositAmount; - uint256 newTotalRewardsAmount = previousRewardsAmount + msg.value; - $.totalRewardDepositAmount = newTotalRewardsAmount; - - emit UpdatedTotalRewardsAmount(previousRewardsAmount, newTotalRewardsAmount, msg.value); - } - - /** - * @notice Reverts the `mintRewards` action. - * @dev Restricted to L1RewardManager - */ - function revertMintRewards(uint256 pufETHAmount, uint256 ethAmount) external restricted { - VaultStorage storage $ = _getPufferVaultStorage(); - - uint256 previousMintAmount = $.totalRewardMintAmount; - // nosemgrep basic-arithmetic-underflow - uint256 newMintAmount = previousMintAmount - ethAmount; - $.totalRewardMintAmount = newMintAmount; - - emit UpdatedTotalRewardsAmount(previousMintAmount, newMintAmount, 0); - - // msg.sender is the L1RewardManager contract - _burn(msg.sender, pufETHAmount); - } -} diff --git a/mainnet-contracts/src/PufferVaultV4.sol b/mainnet-contracts/src/PufferVaultV4.sol deleted file mode 100644 index f2903e13..00000000 --- a/mainnet-contracts/src/PufferVaultV4.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { IStETH } from "./interface/Lido/IStETH.sol"; -import { ILidoWithdrawalQueue } from "./interface/Lido/ILidoWithdrawalQueue.sol"; -import { IWETH } from "./interface/Other/IWETH.sol"; -import { IPufferOracle } from "./interface/IPufferOracle.sol"; -import { PufferVaultV3 } from "./PufferVaultV3.sol"; -import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import { IPufferRevenueDepositor } from "./interface/IPufferRevenueDepositor.sol"; - -/** - * @title PufferVaultV4 - * @dev Implementation of the PufferVault version 4 contract. - * @notice This contract extends the functionality of PufferVaultV3 with additional features for restaking rewards. - * @custom:security-contact security@puffer.fi - * @custom:oz-upgrades-from src/PufferVaultV3.sol:PufferVaultV3 - */ -contract PufferVaultV4 is PufferVaultV3 { - using SafeCast for uint256; - - /** - * @notice The restaking rewards depositor contract. - */ - IPufferRevenueDepositor public immutable RESTAKING_REWARDS_DEPOSITOR; - - /** - * @notice Initializes the PufferVaultV3 contract. - * @param stETH Address of the stETH token contract. - * @param weth Address of the WETH token contract. - * @param lidoWithdrawalQueue Address of the Lido withdrawal queue contract. - * @param oracle Address of the PufferOracle contract. - * @custom:oz-upgrades-unsafe-allow constructor - */ - constructor( - IStETH stETH, - IWETH weth, - ILidoWithdrawalQueue lidoWithdrawalQueue, - IPufferOracle oracle, - IPufferRevenueDepositor revenueDepositor - ) PufferVaultV3(stETH, weth, lidoWithdrawalQueue, oracle) { - RESTAKING_REWARDS_DEPOSITOR = revenueDepositor; - _disableInitializers(); - } - - /** - * @notice Returns the total assets of the vault. - * @dev This is the total assets of the vault minus the pending distribution amount. - * @return The total assets of the vault. - */ - function totalAssets() public view override returns (uint256) { - return super.totalAssets() - RESTAKING_REWARDS_DEPOSITOR.getPendingDistributionAmount(); - } -} diff --git a/mainnet-contracts/src/PufferVaultV5.sol b/mainnet-contracts/src/PufferVaultV5.sol index 1b85629d..38897a5f 100644 --- a/mainnet-contracts/src/PufferVaultV5.sol +++ b/mainnet-contracts/src/PufferVaultV5.sol @@ -19,7 +19,6 @@ import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IPufferOracleV2 } from "./interface/IPufferOracleV2.sol"; import { IPufferVaultV2 } from "./interface/IPufferVaultV2.sol"; import { IPufferRevenueDepositor } from "./interface/IPufferRevenueDepositor.sol"; diff --git a/mainnet-contracts/src/PufferWithdrawalManager.sol b/mainnet-contracts/src/PufferWithdrawalManager.sol index 585473ad..b3a9ff96 100644 --- a/mainnet-contracts/src/PufferWithdrawalManager.sol +++ b/mainnet-contracts/src/PufferWithdrawalManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { PufferVaultV3 } from "./PufferVaultV3.sol"; +import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { IPufferWithdrawalManager } from "./interface/IPufferWithdrawalManager.sol"; import { PufferWithdrawalManagerStorage } from "./PufferWithdrawalManagerStorage.sol"; @@ -32,32 +32,28 @@ contract PufferWithdrawalManager is /** * @notice The batch size for the withdrawal manager - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ - PufferVaultV3 public immutable PUFFER_VAULT; + PufferVaultV5 public immutable PUFFER_VAULT; /** * @notice The minimum withdrawal amount - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ uint256 public constant MIN_WITHDRAWAL_AMOUNT = 0.01 ether; /** * @notice The WETH contract - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ IWETH public immutable WETH; /** * @notice The batch size for the withdrawal manager - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ uint256 public immutable BATCH_SIZE; /** * @dev Constructor to initialize the PufferWithdrawalManager - * @param pufferVault Address of the PufferVaultV3 contract + * @param batchSize The batch size for the withdrawal manager + * @param pufferVault Address of the PufferVaultV5 contract * @param weth Address of the WETH contract - * @custom:oz-upgrades-unsafe-allow constructor */ - constructor(uint256 batchSize, PufferVaultV3 pufferVault, IWETH weth) { + constructor(uint256 batchSize, PufferVaultV5 pufferVault, IWETH weth) { BATCH_SIZE = batchSize; PUFFER_VAULT = pufferVault; WETH = weth; diff --git a/mainnet-contracts/src/ValidatorTicket.sol b/mainnet-contracts/src/ValidatorTicket.sol index 16e820b4..bad4e111 100644 --- a/mainnet-contracts/src/ValidatorTicket.sol +++ b/mainnet-contracts/src/ValidatorTicket.sol @@ -10,7 +10,7 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { ValidatorTicketStorage } from "./ValidatorTicketStorage.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import { PufferVaultV3 } from "./PufferVaultV3.sol"; +import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { IPufferOracle } from "./interface/IPufferOracle.sol"; import { IValidatorTicket } from "./interface/IValidatorTicket.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; @@ -273,7 +273,7 @@ contract ValidatorTicket is uint256 requiredETH = vtAmount.mulDiv(mintPrice, 1 ether, Math.Rounding.Ceil); - pufEthUsed = PufferVaultV3(PUFFER_VAULT).convertToSharesUp(requiredETH); + pufEthUsed = PufferVaultV5(PUFFER_VAULT).convertToSharesUp(requiredETH); IERC20(PUFFER_VAULT).transferFrom(msg.sender, address(this), pufEthUsed); @@ -292,7 +292,7 @@ contract ValidatorTicket is uint256 guardiansAmount = _sendPufETH(OPERATIONS_MULTISIG, pufEthUsed, $.guardiansFeeRate); uint256 burnAmount = pufEthUsed - (treasuryAmount + guardiansAmount); - PufferVaultV3(PUFFER_VAULT).burn(burnAmount); + PufferVaultV5(PUFFER_VAULT).burn(burnAmount); emit DispersedPufETH({ treasury: treasuryAmount, guardians: guardiansAmount, burned: burnAmount }); diff --git a/mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol b/mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol deleted file mode 100644 index f4244f11..00000000 --- a/mainnet-contracts/src/echidna/EchidnaPufferVaultV2.sol +++ /dev/null @@ -1,19 +0,0 @@ -pragma solidity ^0.8.0; - -import { CryticERC4626PropertyTests } from "@crytic/contracts/ERC4626/ERC4626PropertyTests.sol"; -import { PufferVaultV2 } from "../PufferVaultV2.sol"; -import { WETH9 } from "../../test/mocks/WETH9.sol"; -import { stETHMock } from "../../test/mocks/stETHMock.sol"; -import { MockPufferOracle } from "../../test/mocks/MockPufferOracle.sol"; -import { LidoWithdrawalQueueMock } from "../../test/mocks/LidoWithdrawalQueueMock.sol"; - -contract EchidnaPufferVaultV2 is CryticERC4626PropertyTests { - constructor() { - WETH9 weth = new WETH9(); - stETHMock stETH = new stETHMock(); - MockPufferOracle oracle = new MockPufferOracle(); - LidoWithdrawalQueueMock lido = new LidoWithdrawalQueueMock(); - PufferVaultV2 vault = new PufferVaultV2(stETH, weth, lido, oracle); - initialize(address(vault), address(weth), false); - } -} diff --git a/mainnet-contracts/src/echidna/config.yaml b/mainnet-contracts/src/echidna/config.yaml deleted file mode 100644 index 80c30722..00000000 --- a/mainnet-contracts/src/echidna/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -corpusDir: "tests/echidna-corpus" -testMode: assertion -testLimit: 1000000 -deployer: "0x10000" -sender: ["0x10000"] \ No newline at end of file diff --git a/mainnet-contracts/src/interface/IPufferProtocol.sol b/mainnet-contracts/src/interface/IPufferProtocol.sol index 95b61781..f6a87a69 100644 --- a/mainnet-contracts/src/interface/IPufferProtocol.sol +++ b/mainnet-contracts/src/interface/IPufferProtocol.sol @@ -5,7 +5,7 @@ import { Validator } from "../struct/Validator.sol"; import { ValidatorKeyData } from "../struct/ValidatorKeyData.sol"; import { IGuardianModule } from "../interface/IGuardianModule.sol"; import { PufferModuleManager } from "../PufferModuleManager.sol"; -import { PufferVaultV2 } from "../PufferVaultV2.sol"; +import { PufferVaultV5 } from "../PufferVaultV5.sol"; import { IPufferOracleV2 } from "../interface/IPufferOracleV2.sol"; import { Status } from "../struct/Status.sol"; import { Permit } from "../structs/Permit.sol"; @@ -245,7 +245,7 @@ interface IPufferProtocol { /** * @notice Returns the Puffer Vault */ - function PUFFER_VAULT() external view returns (PufferVaultV2); + function PUFFER_VAULT() external view returns (PufferVaultV5); /** * @notice Returns the Puffer Module Manager diff --git a/mainnet-contracts/src/interface/IPufferWithdrawalManager.sol b/mainnet-contracts/src/interface/IPufferWithdrawalManager.sol index 319f05c6..57f750ac 100644 --- a/mainnet-contracts/src/interface/IPufferWithdrawalManager.sol +++ b/mainnet-contracts/src/interface/IPufferWithdrawalManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { PufferVaultV3 } from "../PufferVaultV3.sol"; +import { PufferVaultV5 } from "../PufferVaultV5.sol"; import { Permit } from "../structs/Permit.sol"; import { PufferWithdrawalManagerStorage } from "../PufferWithdrawalManagerStorage.sol"; @@ -120,10 +120,10 @@ interface IPufferWithdrawalManager { event ExcessETHReturned(uint256[] batchIndices, uint256 totalExcessETH); /** - * @notice Returns the address of the PufferVaultV3 contract - * @return The address of the PufferVaultV3 contract + * @notice Returns the address of the PufferVaultV5 contract + * @return The address of the PufferVaultV5 contract */ - function PUFFER_VAULT() external view returns (PufferVaultV3); + function PUFFER_VAULT() external view returns (PufferVaultV5); /** * @notice Returns the minimum withdrawal amount diff --git a/mainnet-contracts/test/Integration/PufferDepositorV2.fork.t.sol b/mainnet-contracts/test/Integration/PufferDepositorV2.fork.t.sol deleted file mode 100644 index 210f894d..00000000 --- a/mainnet-contracts/test/Integration/PufferDepositorV2.fork.t.sol +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; -import { Permit } from "../../src/structs/Permit.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -contract PufferDepositorV2ForkTest is MainnetForkTestHelper { - /** - * @dev Wallet that transferred pufETH to the PufferDepositor by mistake. - */ - address private constant PUFFER = 0x8A0C1e5cEA8e0F6dF341C005335E7fe5ed18A0a0; - - function setUp() public virtual override { - vm.createSelectFork(vm.rpcUrl("mainnet"), 20059956); // https://etherscan.io/block/20059956 - - // Setup contracts that are deployed to mainnet - _setupLiveContracts(); - } - - // StETH deposit through depositor and directly should mint ~amount - function test_stETH_permit_deposit_to_self() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 200 ether) - withCaller(alice) - { - // Deposit amount - uint256 stETHDepositAmount = 100 ether; - Permit memory permit = _signPermit( - _testTemps( - "alice", - address(pufferDepositor), - stETHDepositAmount, - block.timestamp, - hex"260e7e1a220ea89b9454cbcdc1fcc44087325df199a3986e560d75db18b2e253" - ) - ); - - // PufferDepositor deposit - uint256 depositorAmount = pufferDepositor.depositStETH(permit, alice); - - assertEq(depositorAmount, pufferVault.balanceOf(alice), "alice got the tokens"); - - stETH.approve(address(pufferVault), stETHDepositAmount); - - uint256 stETHSharesAmount = _ST_ETH.getSharesByPooledEth(stETHDepositAmount); - - // Direct deposit to the Vault - uint256 directDepositAmount = pufferVault.depositStETH(stETHSharesAmount, alice); - - uint256 depositorAssetsAmount = pufferVault.convertToAssets(depositorAmount); - uint256 directDepositAssetsAmount = pufferVault.convertToAssets(directDepositAmount); - - assertApproxEqAbs(pufferVault.convertToAssets(depositorAmount), depositorAssetsAmount, 2, "depositor"); - assertApproxEqAbs( - pufferVault.convertToAssets(directDepositAmount), directDepositAssetsAmount, 2, "direct deposit" - ); - - assertApproxEqAbs( - depositorAssetsAmount + directDepositAssetsAmount, - 2 * stETHDepositAmount, - 5, // 5 wei difference - "should have ~200 eth worth of assets" - ); - - assertApproxEqAbs(depositorAmount, directDepositAmount, 2, "depositor amount should be ~direct deposit amount"); - assertApproxEqAbs(depositorAssetsAmount, directDepositAssetsAmount, 2, "received assets should be ~equal"); - assertApproxEqAbs(depositorAssetsAmount, stETHDepositAmount, 2, "steth received assets should be ~equal"); - } - - function test_stETH_donation_and_first_depositor_after_donation() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 100 ether) - giveToken(BLAST_DEPOSIT, address(stETH), bob, 100 ether) - withCaller(alice) - { - // Alice transfers 1 stETH to the Vault by mistake - _ST_ETH.transfer(address(pufferDepositor), 1 ether); - - assertEq(pufferVault.balanceOf(alice), 0, "0 pufETH for alice"); - - vm.startPrank(bob); - - Permit memory permit = _signPermit( - _testTemps( - "bob", - address(pufferDepositor), - 1 ether, - block.timestamp, - hex"260e7e1a220ea89b9454cbcdc1fcc44087325df199a3986e560d75db18b2e253" - ) - ); - assertEq(0, pufferVault.balanceOf(bob), "bob got 0 pufETH"); - - // 1 stETH should be ~ - uint256 expectedAmount = pufferVault.convertToShares(1 ether); - - // Bob deposits 1 stETH via PufferDepositor - // But his deposit will sweep the stETH.balanceOf(pufferDepositor) as well, meaning he will get shares for Alice's 1 stETH - uint256 depositorAmount = pufferDepositor.depositStETH(permit, bob); - - assertEq(depositorAmount, pufferVault.balanceOf(bob), "bob got"); - - // 3 wei difference, because the PufferDepositor already has 1 wei of stETH (leftover) - assertApproxEqAbs((expectedAmount * 2), depositorAmount, 3, "bob got more pufETH than expected"); - - assertApproxEqAbs( - pufferVault.convertToAssets(pufferVault.balanceOf(bob)), 2 ether, 3, "2 eth worth of assets for bob" - ); - - assertEq(0, pufferVault.balanceOf(alice), "alice got 0"); - } - - function test_stETH_share_conversion() public view { - uint256 stETHAmount = 100 ether; - uint256 stETHSharesAmount = _ST_ETH.getSharesByPooledEth(stETHAmount); - uint256 stETHAmountFromShares = _ST_ETH.getPooledEthByShares(stETHSharesAmount); - - assertApproxEqAbs(stETHAmount, stETHAmountFromShares, 1, "stETH amount should be ~stETH amount from shares"); - } - - function test_stETH_permit_deposit_to_bob() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 200 ether) - withCaller(alice) - { - Permit memory permit = _signPermit( - _testTemps( - "alice", - address(pufferDepositor), - 100 ether, - block.timestamp, - hex"260e7e1a220ea89b9454cbcdc1fcc44087325df199a3986e560d75db18b2e253" - ) - ); - - uint256 depositorAmount = pufferDepositor.depositStETH(permit, bob); - - assertEq(depositorAmount, pufferVault.balanceOf(bob), "bob got the tokens"); - assertEq(0, pufferVault.balanceOf(alice), "alice got 0"); - } - - // stETH approve deposit - function test_stETH_approve_deposit_to_self() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 200 ether) - withCaller(alice) - { - uint256 stETHAmount = 100 ether; - // Create an unsigned permit to call function - Permit memory unsignedPermit = Permit(0, stETHAmount, 0, 0, 0); - IERC20(address(stETH)).approve(address(pufferDepositor), stETHAmount); - - uint256 depositorAmount = pufferDepositor.depositStETH(unsignedPermit, alice); - - assertEq(depositorAmount, pufferVault.balanceOf(alice), "alice got the tokens"); - - // StETH deposit through depositor and directly should mint the same amount - stETH.approve(address(pufferVault), stETHAmount); - - uint256 stETHSharesAmount = _ST_ETH.getSharesByPooledEth(stETHAmount); - - uint256 directDepositAmount = pufferVault.depositStETH(stETHSharesAmount, alice); - - uint256 depositorAssetsAmount = pufferVault.convertToAssets(depositorAmount); - uint256 directDepositAssetsAmount = pufferVault.convertToAssets(directDepositAmount); - - assertApproxEqAbs(depositorAmount, directDepositAmount, 2, "1 wei difference"); - assertApproxEqAbs(depositorAssetsAmount, directDepositAssetsAmount, 2, "received assets should be ~equal"); - assertApproxEqAbs( - depositorAssetsAmount, stETHAmount, 2, "amount deposited and convertToAssets should be ~equal" - ); - } - - // stETH approve deposit to bob - function test_stETH_approve_deposit_to_bob() - public - giveToken(BLAST_DEPOSIT, address(stETH), alice, 200 ether) - withCaller(alice) - { - uint256 stETHAmount = 100 ether; - // Create an unsigned permit to call function - Permit memory unsignedPermit = Permit(0, stETHAmount, 0, 0, 0); - IERC20(address(stETH)).approve(address(pufferDepositor), stETHAmount); - - uint256 depositorAmount = pufferDepositor.depositStETH(unsignedPermit, bob); - - assertEq(depositorAmount, pufferVault.balanceOf(bob), "bob got the tokens"); - assertEq(0, pufferVault.balanceOf(alice), "alice got 0"); - } - - // wstETH permit deposit - function test_wstETH_permit_deposit() - public - giveToken(0x0B925eD163218f6662a35e0f0371Ac234f9E9371, address(_WST_ETH), alice, 1 ether) - withCaller(alice) - { - Permit memory permit = _signPermit( - _testTemps( - "alice", - address(pufferDepositor), - 1 ether, - block.timestamp, - hex"d4a8ff90a402dc7d4fcbf60f5488291263c743ccff180e139f47d139cedfd5fe" - ) - ); - uint256 received = pufferDepositor.depositWstETH(permit, alice); - assertEq(received, pufferVault.balanceOf(alice), "alice got 0"); - } - - // wstETH permit deposit to bob - function test_wstETH_permit_deposit_to_bob() - public - giveToken(0x0B925eD163218f6662a35e0f0371Ac234f9E9371, address(_WST_ETH), alice, 1 ether) - withCaller(alice) - { - Permit memory permit = _signPermit( - _testTemps( - "alice", - address(pufferDepositor), - 1 ether, - block.timestamp, - hex"d4a8ff90a402dc7d4fcbf60f5488291263c743ccff180e139f47d139cedfd5fe" - ) - ); - uint256 received = pufferDepositor.depositWstETH(permit, bob); - - assertEq(received, pufferVault.balanceOf(bob), "bob got the tokens"); - assertEq(0, pufferVault.balanceOf(alice), "alice got 0"); - } - - // wstETH approve deposit - function test_wstETH_approve_deposit_to_self() - public - giveToken(0x0B925eD163218f6662a35e0f0371Ac234f9E9371, address(_WST_ETH), alice, 1 ether) - withCaller(alice) - { - // Create an unsigned permit to call function - Permit memory unsignedPermit = Permit(0, 1 ether, 0, 0, 0); - IERC20(address(_WST_ETH)).approve(address(pufferDepositor), 1 ether); - uint256 received = pufferDepositor.depositWstETH(unsignedPermit, alice); - - assertEq(received, pufferVault.balanceOf(alice), "alice got the tokens"); - } - - // wstETH approve deposit to bob - function test_wstETH_approve_deposit_to_bob() - public - giveToken(0x0B925eD163218f6662a35e0f0371Ac234f9E9371, address(_WST_ETH), alice, 1 ether) - withCaller(alice) - { - // Create an unsigned permit to call function - Permit memory unsignedPermit = Permit(0, 1 ether, 0, 0, 0); - IERC20(address(_WST_ETH)).approve(address(pufferDepositor), 1 ether); - uint256 received = pufferDepositor.depositWstETH(unsignedPermit, bob); - - assertEq(received, pufferVault.balanceOf(bob), "bob got the tokens"); - assertEq(0, pufferVault.balanceOf(alice), "alice got 0"); - } -} diff --git a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol b/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol deleted file mode 100644 index 8c1ad70f..00000000 --- a/mainnet-contracts/test/Integration/PufferRevenueDepositor.fork.t.sol +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; -import { DeployRevenueDepositor } from "../../script/DeployRevenueDepositor.s.sol"; -import { PufferRevenueDepositor } from "../../src/PufferRevenueDepositor.sol"; -import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { ROLE_ID_REVENUE_DEPOSITOR } from "../../script/Roles.sol"; -import { IPufferRevenueDepositor } from "../../src/interface/IPufferRevenueDepositor.sol"; -import { PufferVaultV4 } from "../../src/PufferVaultV4.sol"; -import { IStETH } from "../../src/interface/Lido/IStETH.sol"; -import { IWETH } from "../../src/interface/Other/IWETH.sol"; -import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; - -struct AssetValue { - IERC20 asset; - uint256 value; -} - -interface IAeraVault { - function withdraw(AssetValue[] calldata amounts) external; -} - -contract PufferRevenueDepositorForkTest is MainnetForkTestHelper { - PufferRevenueDepositor public revenueDepositor; - - uint24 public constant REWARDS_DISTRIBUTION_WINDOW = 1 days; - - function setUp() public virtual override { - // Cancun upgrade - vm.createSelectFork(vm.rpcUrl("mainnet"), 21112808); // (Nov-04-2024 07:31:35 AM +UTC) - - // Setup contracts that are deployed to mainnet - _setupLiveContracts(); - - // Deploy revenue depositor - DeployRevenueDepositor depositorDeployer = new DeployRevenueDepositor(); - depositorDeployer.run(); - revenueDepositor = depositorDeployer.revenueDepositor(); - - // Upgrade PufferVault to V4 - address newVault = address( - new PufferVaultV4( - IStETH(_getStETH()), - IWETH(_getWETH()), - ILidoWithdrawalQueue(_getLidoWithdrawalQueue()), - IPufferOracle(_getPufferOracle()), - revenueDepositor - ) - ); - - // Setup AccessManager - vm.startPrank(_getTimelock()); - - // Upgrade PufferVault to V4 - pufferVault.upgradeToAndCall(newVault, ""); - - (bool success,) = address(accessManager).call(depositorDeployer.encodedCalldata()); - assertTrue(success, "Failed to deploy revenue depositor"); - - // Transfer ownership of Aera vault to revenue depositor - vm.startPrank(_getOPSMultisig()); - Ownable2Step(_getAeraVault()).transferOwnership(address(revenueDepositor)); - - // Accept ownership of Aera vault - address[] memory targets = new address[](1); - targets[0] = address(_getAeraVault()); - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeCall(Ownable2Step.acceptOwnership, ()); - - revenueDepositor.callTargets(targets, data); - - // Grant the revenue depositor role to the revenue depositor itesels so that we can use callTargets to withdraw & deposit in 1 tx - vm.startPrank(_getTimelock()); - accessManager.grantRole(ROLE_ID_REVENUE_DEPOSITOR, address(revenueDepositor), 0); - - // Set rewards distribution window to 1 day - vm.startPrank(_getDAO()); - revenueDepositor.setRewardsDistributionWindow(REWARDS_DISTRIBUTION_WINDOW); - - vm.stopPrank(); - } - - function test_sanity() public view { - assertTrue(address(revenueDepositor) != address(0), "Revenue depositor not deployed"); - assertEq( - pufferVault.convertToAssets(1 ether), - 1.026019081620562074 ether, - "1 pufETH should be 1.026019081620562074 WETH" - ); - } - - function test_deposit_revenue() public { - vm.startPrank(_getOPSMultisig()); - - uint256 wethBalance = IERC20(_getWETH()).balanceOf(_getAeraVault()); - - AssetValue[] memory assets = new AssetValue[](1); - assets[0] = AssetValue({ asset: IERC20(_getWETH()), value: wethBalance }); - - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeCall(IAeraVault(_getAeraVault()).withdraw, (assets)); - - address[] memory targets = new address[](1); - targets[0] = address(_getAeraVault()); - - revenueDepositor.callTargets(targets, data); - - vm.expectEmit(true, true, true, true); - emit IPufferRevenueDepositor.RevenueDeposited(wethBalance); - revenueDepositor.depositRevenue(); - } - - function test_withdrawAndDeposit() public { - vm.startPrank(_getOPSMultisig()); - - uint256 wethBalance = IERC20(_getWETH()).balanceOf(_getAeraVault()); - - vm.expectEmit(true, true, true, true); - emit IPufferRevenueDepositor.RevenueDeposited(wethBalance); - revenueDepositor.withdrawAndDeposit(); - } - - // Deposit revenue from Aera Vault to Puffer Vault - function test_deposit_weth_to_puffer_vault() public { - vm.startPrank(_getOPSMultisig()); - - address[] memory targets = new address[](2); - targets[0] = address(_getAeraVault()); - targets[1] = address(revenueDepositor); - - uint256 wethBalance = IERC20(_getWETH()).balanceOf(_getAeraVault()); - - uint256 vaultWethBalance = IERC20(_getWETH()).balanceOf(address(pufferVault)); - uint256 pufferVaultAssetsBefore = pufferVault.totalAssets(); - - assertEq(pufferVaultAssetsBefore, 317212543571614106164392, "Puffer Vault Assets before"); - assertEq(vaultWethBalance, 0, "Puffer Vault should have 0 WETH before deposit"); - assertEq(wethBalance, 67.612355147076514123 ether, "Aera Vault should have 67.612355147076514123 WETH"); - - AssetValue[] memory assets = new AssetValue[](1); - assets[0] = AssetValue({ asset: IERC20(_getWETH()), value: wethBalance }); - - // 1. Withdraw WETH from Aera Vault -> Owner (Revenue Depositor) - // 2. Deposit WETH into Puffer Vault -> Revenue Depositor - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeCall(IAeraVault(_getAeraVault()).withdraw, (assets)); - data[1] = abi.encodeCall(revenueDepositor.depositRevenue, ()); - - vm.expectEmit(true, true, true, true); - emit IPufferRevenueDepositor.RevenueDeposited(wethBalance); - revenueDepositor.callTargets(targets, data); - - uint256 wethBalanceAfter = IERC20(_getWETH()).balanceOf(_getAeraVault()); - assertEq(wethBalanceAfter, 0, "Aera Vault should have 0 WETH after deposit"); - - assertEq( - IERC20(_getWETH()).balanceOf(address(pufferVault)), - vaultWethBalance + wethBalance, - "Puffer Vault received WETH" - ); - - // Assets after in the same block are the same - assertEq(pufferVault.totalAssets(), pufferVaultAssetsBefore, "Puffer Vault total assets after"); - - vm.warp(block.timestamp + REWARDS_DISTRIBUTION_WINDOW); - - // Everything is deposited - assertEq( - pufferVault.totalAssets(), - pufferVaultAssetsBefore + wethBalance, - "Puffer Vault total assets after everything is deposited" - ); - } -} diff --git a/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol deleted file mode 100644 index 358da6e6..00000000 --- a/mainnet-contracts/test/Integration/PufferVaultV2.fork.t.sol +++ /dev/null @@ -1,582 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { ERC4626Upgradeable } from "@openzeppelin-contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; -import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IPufferVaultV2 } from "../../src/interface/IPufferVaultV2.sol"; -import { ROLE_ID_DAO, ROLE_ID_PUFFER_PROTOCOL } from "../../script/Roles.sol"; -import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; - -contract PufferVaultV2ForkTest is MainnetForkTestHelper { - // Puffers that send pufETH by mistake - address private constant WHALE_PUFFER = 0xe6957D9b493b2f2634c8898AC09dc14Cb24BE222; - address private constant PUFFER = 0x34c912C13De7953530DBE4c32F597d1bAF77889b; - - address pufferWhale = 0xd164B614FdE7939078c7558F9680FA32f01aed77; - - function setUp() public virtual override { - // Cancun upgrade - vm.createSelectFork(vm.rpcUrl("mainnet"), 19431593); //(Mar-14-2024 06:53:11 AM +UTC) - - // Setup contracts that are deployed to mainnet - _setupLiveContracts(); - - // Simulate transferring pufETH to the PufferVault by mistake - _giveToken( - 0xe6957D9b493b2f2634c8898AC09dc14Cb24BE222, - address(pufferVault), - address(pufferVault), - 299.864287100672938618 ether - ); - - assertEq(pufferVault.balanceOf(address(pufferVault)), 299.889713214250445236 ether, "pufferVault pufETH"); - assertEq(pufferVault.balanceOf(WHALE_PUFFER), 0, "WHALE_PUFFER pufETH before"); - assertEq(pufferVault.balanceOf(PUFFER), 0, "PUFFER pufETH before"); - - // Upgrade to latest version - _upgradeToMainnetPuffer(); - - assertEq(pufferVault.balanceOf(address(pufferVault)), 0, "vault pufETH"); - assertEq(pufferVault.balanceOf(WHALE_PUFFER), 299.864287100672938618 ether, "WHALE_PUFFER pufETH after"); - assertEq(pufferVault.balanceOf(PUFFER), 0.025426113577506618 ether, "PUFFER pufETH after"); - } - - // Sanity check - function test_sanity() public view { - assertEq(pufferVault.name(), "pufETH", "name"); - assertEq(pufferVault.symbol(), "pufETH", "symbol"); - assertEq(pufferVault.decimals(), 18, "decimals"); - assertEq(pufferVault.asset(), address(_WETH), "asset"); - assertEq(pufferVault.getPendingLidoETHAmount(), 0, "0 pending lido eth"); - assertEq(pufferVault.totalAssets(), 368072.286049064583783628 ether, "total assets"); - assertEq(pufferVault.getExitFeeBasisPoints(), 100, "1% withdrawal fee"); - } - - // Deposit & Withdrawal in the same tx is forbidden. This is a security measure to prevent vault griefing by using flash loans. - function test_deposit_and_withdrawal_same_tx() public withCaller(alice) { - // In test environment, we deploy and use src/PufferVaultV2Tests.sol that has the markDeposit modifier disabled - // Foundry tests are executing all tests from the same transaction, and if it wasn't disabled, pretty much every test would fail. - - // With that code PufferVaultV2Tests deployed, we can test the deposit and withdrawal in the same transaction - vm.deal(alice, 2 ether); - pufferVault.depositETH{ value: 1 ether }(alice); - pufferVault.withdraw(pufferVault.maxWithdraw(alice), alice, alice); - - // After we made sure that it works, we can re-enable the modifier by upgrading to a real mainnet `PufferVaultV2.sol` that has the modifier enabled - vm.startPrank(COMMUNITY_MULTISIG); - UUPSUpgradeable(pufferVault).upgradeToAndCall(address(pufferVaultWithBlocking), ""); - - // Now, in the same transaction Alice deposits successfully, but the withdrawal reverts - vm.startPrank(alice); - pufferVault.depositETH{ value: 1 ether }(alice); - - uint256 maxWithdraw = pufferVault.maxWithdraw(alice); - - // Withdrawal reverts because it is in the same transaction (foundry tests are executing all tests from the same transaction) - vm.expectRevert(abi.encodeWithSelector(IPufferVaultV2.DepositAndWithdrawalForbidden.selector)); - pufferVault.withdraw(maxWithdraw, alice, alice); - } - - function test_max_deposit() public giveToken(MAKER_VAULT, address(_WETH), alice, 100 ether) { - assertEq(pufferVault.maxDeposit(alice), type(uint256).max, "max deposit"); - } - - function test_set_exit_fee_change() public { - // Get liquidity - _withdraw_stETH_from_lido(); - - // Unauthorized - vm.expectRevert(); - pufferVault.setExitFeeBasisPoints(200); - - // Default value is 1% - assertEq(pufferVault.getExitFeeBasisPoints(), 100, "1% withdrawal fee"); - - uint256 sharesRequiredBefore = pufferVault.previewWithdraw(10 ether); - - // Timelock.sol is the admin of AccessManager - vm.startPrank(address(timelock)); - vm.expectEmit(true, true, true, true); - emit IPufferVaultV2.ExitFeeBasisPointsSet(100, 200); - pufferVault.setExitFeeBasisPoints(200); - - // After - assertEq(pufferVault.getExitFeeBasisPoints(), 200, "2% withdrawal fee"); - - // Because it is a bigger fee, the shares required to withdraw 100 ETH is bigger - uint256 sharesRequiredAfter = pufferVault.previewWithdraw(10 ether); - assertGt(sharesRequiredAfter, sharesRequiredBefore, "shares required before must be bigger"); - - // Withdraw assets - vm.startPrank(pufferWhale); - uint256 sharesWithdrawn = pufferVault.withdraw(10 ether, pufferWhale, pufferWhale); - - vm.startPrank(address(timelock)); - vm.expectEmit(true, true, true, true); - emit IPufferVaultV2.ExitFeeBasisPointsSet(200, 0); - pufferVault.setExitFeeBasisPoints(0); - - assertEq(pufferVault.getExitFeeBasisPoints(), 0, "0"); - - // Withdraw the same amount of assets again - vm.startPrank(pufferWhale); - uint256 sharesWithdrawnAfter = pufferVault.withdraw(10 ether, pufferWhale, pufferWhale); - - assertLt(sharesWithdrawnAfter, sharesWithdrawn, "no fee = less shares needed"); - } - - function test_max_withdrawal() public giveToken(MAKER_VAULT, address(_WETH), alice, 100 ether) { - // Alice doesn't have any pufETH - assertEq(pufferVault.maxWithdraw(alice), 0, "max withdraw"); - assertEq(pufferVault.maxRedeem(alice), 0, "max maxRedeem"); - - // Whale has more than 100 ether, but the limit is 100 eth - assertEq(pufferVault.maxWithdraw(pufferWhale), 100 ether, "max withdraw"); - // Because of the withdrawal fee, the maxRedeem is bigger than the maxWithdraw - assertEq(pufferVault.maxRedeem(pufferWhale), 100.595147442558494386 ether, "max redeem"); - } - - function test_withdraw_fee() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - address recipient = makeAddr("assetsRecipient"); - - uint256 expectedSharesWithdrawn = pufferVault.previewWithdraw(10 ether); - - assertEq(_WETH.balanceOf(recipient), 0, "got 0 weth"); - - // Withdraw - vm.startPrank(pufferWhale); - uint256 sharesWithdrawn = pufferVault.withdraw(10 ether, recipient, pufferWhale); - vm.stopPrank(); - - // Recipient will get 10 WETH - assertEq(_WETH.balanceOf(recipient), 10 ether, "got +10 weth"); - - assertEq(expectedSharesWithdrawn, sharesWithdrawn, "must match"); - - // The exchange rate changes after the first withdrawal, because of the fee - // The second withdrawal will burn less shares than the first one - uint256 expectedShares = pufferVault.previewWithdraw(10 ether); - - assertLt(expectedShares, sharesWithdrawn, "shares must be less than previous"); - - vm.startPrank(pufferWhale); - pufferVault.redeem(expectedShares, recipient, pufferWhale); - - assertEq(_WETH.balanceOf(recipient), 20 ether, "+10 weth"); - } - - function test_redemption_fee() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - address recipient = makeAddr("assetsRecipient"); - - // This much shares will get us 10 WETH - uint256 expectedSharesWithdrawn = pufferVault.previewWithdraw(10 ether); - - uint256 expectedAssetsOut = pufferVault.previewRedeem(expectedSharesWithdrawn); - - assertEq(_WETH.balanceOf(recipient), 0, "got 0 weth"); - - // // Withdraw - vm.startPrank(pufferWhale); - uint256 assetsOut = pufferVault.redeem(expectedSharesWithdrawn, recipient, pufferWhale); - vm.stopPrank(); - - // // // Recipient will get 10 WETH - assertEq(_WETH.balanceOf(recipient), 10 ether, "got +10 weth"); - - assertEq(expectedAssetsOut, assetsOut, "must match"); - assertEq(assetsOut, 10 ether, "must match eth"); - - // The exchange rate changes slightly after the first withdrawal, because of the withdrawal fee - // The same amount of - uint256 expectedAssets = pufferVault.previewRedeem(expectedSharesWithdrawn); - - assertGt(expectedAssets, 10 ether, "second withdrawal previewRedeem"); - - vm.startPrank(pufferWhale); - pufferVault.redeem(expectedSharesWithdrawn, recipient, pufferWhale); - - uint256 recipientBalance = _WETH.balanceOf(recipient); - - assertGt(recipientBalance, 20 ether, "+10 weth"); - } - - function test_withdrawal() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - vm.startPrank(pufferWhale); - - assertEq(pufferVault.maxWithdraw(pufferWhale), 100 ether, "max withdraw"); - pufferVault.withdraw(50 ether, pufferWhale, pufferWhale); - - assertEq(pufferVault.maxWithdraw(pufferWhale), 50 ether, "leftover max withdraw"); - - pufferVault.withdraw(50 ether, pufferWhale, pufferWhale); - assertEq(pufferVault.maxWithdraw(pufferWhale), 0 ether, "no leftover max withdraw"); - } - - function test_withdrawal_transfers_to_receiver() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - // Initial state - assertEq(_WETH.balanceOf(address(alice)), 0, "alice balance"); - uint256 whaleShares = pufferVault.balanceOf(pufferWhale); - - // Withdraw with alice as receiver - vm.startPrank(pufferWhale); - uint256 sharesBurned = pufferVault.withdraw({ assets: 50 ether, receiver: alice, owner: pufferWhale }); - vm.stopPrank(); - - // Alice received 50 wETH - assertEq(_WETH.balanceOf(address(alice)), 50 ether, "alice balance"); - - // Whale burned shares - assertApproxEqAbs(pufferVault.balanceOf(pufferWhale), whaleShares - sharesBurned, 1e9, "asset change"); - } - - function test_withdrawal_succeeds_with_allowance() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - // Initial state - assertEq(_WETH.balanceOf(address(alice)), 0, "alice balance"); - uint256 whaleShares = pufferVault.balanceOf(pufferWhale); - - // pufferWhale approves alice to burn their pufETH - vm.startPrank(pufferWhale); - pufferVault.approve(address(alice), type(uint256).max); - vm.stopPrank(); - - // Alice tries to withdraw on behalf of pufferWhale - vm.startPrank(alice); - uint256 sharesBurned = pufferVault.withdraw({ assets: 50 ether, receiver: alice, owner: pufferWhale }); - vm.stopPrank(); - - // Alice should receives 50 wETH - assertEq(_WETH.balanceOf(address(alice)), 50 ether, "alice balance"); - - // Whale burned shares - assertApproxEqAbs(pufferVault.balanceOf(pufferWhale), whaleShares - sharesBurned, 1e9, "asset change"); - } - - function test_withdrawal_fails_if_owner_is_not_caller() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - // Initial state - assertEq(_WETH.balanceOf(address(alice)), 0, "alice balance"); - - // Alice tries to withdraw on behalf of pufferWhale - vm.startPrank(alice); - vm.expectRevert(); - pufferVault.withdraw({ assets: 50 ether, receiver: alice, owner: pufferWhale }); - vm.stopPrank(); - - // Alice should not receive - assertEq(_WETH.balanceOf(address(alice)), 0 ether, "alice balance"); - } - - function test_withdrawal_fails_when_exceeding_maximum() - public - giveToken(MAKER_VAULT, address(_WETH), alice, 100 ether) - { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - vm.startPrank(alice); - - // vm.expectRevert(abi.encodeWithSelector(ERC4626Upgradeable.ERC4626ExceededMaxWithdraw.selector, alice, 100 ether + 1)); // failing to encode correctly - vm.expectRevert(); - pufferVault.withdraw(100 ether + 1, alice, alice); - } - - // deposit WETH - function test_deposit() public giveToken(MAKER_VAULT, address(_WETH), alice, 100 ether) withCaller(alice) { - uint256 depositAmount = 100 ether; - uint256 estimatedShares = pufferVault.previewDeposit(depositAmount); - uint256 assetsBefore = pufferVault.totalAssets(); - uint256 sharesBefore = pufferVault.totalSupply(); - _WETH.approve(address(pufferVault), type(uint256).max); - uint256 gotShares = pufferVault.deposit(depositAmount, alice); - assertEq(estimatedShares, gotShares, "shares"); - assertLt(gotShares, depositAmount, "shares must be less than deposit"); - assertApproxEqAbs(pufferVault.totalAssets(), assetsBefore + depositAmount, 1e9, "asset change"); - assertApproxEqAbs(pufferVault.totalSupply(), sharesBefore + estimatedShares, 1e9, "shares change"); - } - - function test_deposit_fails_when_not_enough_funds() public { - vm.expectRevert(); - pufferVault.deposit(100 ether + 1, alice); - - vm.expectRevert(); - pufferVault.depositETH{ value: type(uint256).max }(alice); - - vm.expectRevert(); - pufferVault.depositStETH(100 ether + 1, alice); - } - - function test_burn() public withCaller(pufferWhale) { - vm.expectRevert(); - pufferVault.burn(100 ether); - // Grant PufferProtocol role to the whale, because he has tokens to burn - vm.startPrank(address(timelock)); - accessManager.grantRole(ROLE_ID_PUFFER_PROTOCOL, pufferWhale, 0); - - // burn works - vm.startPrank(pufferWhale); - - uint256 balanceBefore = pufferVault.balanceOf(pufferWhale); - - vm.expectEmit(true, true, true, true); - emit IERC20.Transfer(pufferWhale, address(0), 100 ether); - pufferVault.burn(100 ether); - - uint256 balanceAfter = pufferVault.balanceOf(pufferWhale); - assertEq(balanceAfter, balanceBefore - 100 ether, "balance"); - } - - function test_transferETH() public { - // Give ETH liquidity - _withdraw_stETH_from_lido(); - - address mockProtocol = makeAddr("mockProtocol"); - - // This contract has no ROLE_ID_PUFFER_PROTOCOL, so this reverts - vm.expectRevert(); - pufferVault.transferETH(mockProtocol, 10 ether); - - // Grant Protocol role to mockProtocol address - vm.startPrank(address(timelock)); - accessManager.grantRole(ROLE_ID_PUFFER_PROTOCOL, mockProtocol, 0); - - assertEq(mockProtocol.balance, 0 ether, "protocol ETH"); - - vm.startPrank(mockProtocol); - vm.expectEmit(true, true, true, true); - emit IPufferVaultV2.TransferredETH(mockProtocol, 10 ether); - pufferVault.transferETH(mockProtocol, 10 ether); - - assertEq(mockProtocol.balance, 10 ether, "protocol ETH after"); - } - - function test_transferETH_with_weth_liquidity() public giveToken(MAKER_VAULT, address(_WETH), alice, 100 ether) { - // NO ETH liquidity, but we have WETH - - address mockProtocol = makeAddr("mockProtocol"); - - // Grant Protocol role to mockProtocol address - vm.startPrank(address(timelock)); - accessManager.grantRole(ROLE_ID_PUFFER_PROTOCOL, mockProtocol, 0); - - assertEq(mockProtocol.balance, 0 ether, "protocol ETH"); - - vm.startPrank(mockProtocol); - vm.expectRevert(); - pufferVault.transferETH(mockProtocol, 10 ether); - - // Alice deposits 100 WETH - vm.startPrank(alice); - _WETH.approve(address(pufferVault), type(uint256).max); - pufferVault.deposit(100 ether, alice); - - // Alice tries to transferETH, got no permissions - vm.expectRevert(); - pufferVault.transferETH(mockProtocol, 10 ether); - - // Now it works - vm.startPrank(mockProtocol); - pufferVault.transferETH{ gas: 800000 }(mockProtocol, 10 ether); - - // assertEq(mockProtocol.balance, 10 ether, "protocol ETH after"); - } - - function test_redeem_fails_if_no_eth_seeded() public withCaller(pufferWhale) { - // mainnet vault start actually has some balance - assertEq(address(pufferVault).balance, 4433776828572703, "vault ETH"); - - uint256 maxWhaleRedeemableShares = pufferVault.maxRedeem(pufferWhale); - - vm.expectRevert(); - pufferVault.redeem(maxWhaleRedeemableShares, pufferWhale, pufferWhale); - } - - // function test_redeem_succeeds_if_seeded_with_eth() public withCaller(pufferWhale) { - function test_redeem_succeeds_if_seeded_with_eth() public { - // mainnet vault start with 0 eth - assertEq(address(pufferVault).balance, 4433776828572703, "vault ETH"); - - // Fill vault with withdrawal liquidity - _withdraw_stETH_from_lido(); - - // before state - uint256 assetsBefore = pufferVault.totalAssets(); - uint256 sharesBefore = pufferVault.totalSupply(); - uint256 whaleShares = pufferVault.balanceOf(pufferWhale); - - // redeem all of whale's shares - vm.startPrank(pufferWhale); - uint256 maxWhaleRedeemableShares = pufferVault.maxRedeem(pufferWhale); - uint256 redeemedAssets = pufferVault.redeem(maxWhaleRedeemableShares, pufferWhale, pufferWhale); - vm.stopPrank(); - - // no more to redeem - assertEq(pufferVault.maxRedeem(pufferWhale), 0, "max redeem"); - - // vault's assets are reduced - assertApproxEqAbs(pufferVault.totalAssets(), assetsBefore - redeemedAssets, 1e9, "asset change"); - // vault's shares are reduced - assertApproxEqAbs(pufferVault.totalSupply(), sharesBefore - maxWhaleRedeemableShares, 1e9, "shares change"); - // whale's shares are reduced - assertApproxEqAbs( - pufferVault.balanceOf(pufferWhale), whaleShares - maxWhaleRedeemableShares, 1e9, "shares change" - ); - } - - function test_redeem_transfers_to_receiver() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - // Initial state - assertEq(_WETH.balanceOf(address(alice)), 0, "alice balance"); - uint256 whaleShares = pufferVault.balanceOf(pufferWhale); - - // Withdraw with alice as receiver - vm.startPrank(pufferWhale); - uint256 assets = pufferVault.redeem({ shares: 50 ether, receiver: alice, owner: pufferWhale }); - vm.stopPrank(); - - // Alice received 50 wETH - assertEq(_WETH.balanceOf(address(alice)), assets, "alice balance"); - - // Whale burned shares - assertApproxEqAbs(pufferVault.balanceOf(pufferWhale), whaleShares - 50 ether, 1e9, "asset change"); - } - - function test_redeem_succeeds_with_allowance() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - // Initial state - assertEq(_WETH.balanceOf(address(alice)), 0, "alice balance"); - uint256 whaleShares = pufferVault.balanceOf(pufferWhale); - - // pufferWhale approves alice to burn their pufETH - vm.startPrank(pufferWhale); - pufferVault.approve(address(alice), type(uint256).max); - vm.stopPrank(); - - // Alice tries to withdraw on behalf of pufferWhale - vm.startPrank(alice); - uint256 assets = pufferVault.redeem({ shares: 50 ether, receiver: alice, owner: pufferWhale }); - vm.stopPrank(); - - // Alice should receives 50 wETH - assertEq(_WETH.balanceOf(address(alice)), assets, "alice balance"); - assertApproxEqAbs(pufferVault.balanceOf(pufferWhale), whaleShares - 50 ether, 1e9, "asset change"); - } - - function test_redeem_fails_if_owner_is_not_caller() public { - // Get withdrawal liquidity - _withdraw_stETH_from_lido(); - - // Initial state - assertEq(_WETH.balanceOf(address(alice)), 0, "alice balance"); - - // Alice tries to withdraw on behalf of pufferWhale - vm.startPrank(alice); - vm.expectRevert(); - pufferVault.redeem({ shares: 50 ether, receiver: alice, owner: pufferWhale }); - vm.stopPrank(); - - // Alice should not receive - assertEq(_WETH.balanceOf(address(alice)), 0 ether, "alice balance"); - } - - // mint with WETH - function test_mint() public giveToken(MAKER_VAULT, address(_WETH), alice, 100 ether) withCaller(alice) { - uint256 sharesAmount = 5 ether; - uint256 estimatedAssets = pufferVault.previewMint(5 ether); - uint256 assetsBefore = pufferVault.totalAssets(); - uint256 sharesBefore = pufferVault.totalSupply(); - _WETH.approve(address(pufferVault), type(uint256).max); - uint256 gotAssets = pufferVault.mint(sharesAmount, alice); - assertEq(estimatedAssets, gotAssets, "got assets"); - assertLt(sharesAmount, gotAssets, "shares must be less than deposit"); - assertApproxEqAbs(pufferVault.totalAssets(), assetsBefore + estimatedAssets, 1e9, "asset change"); - assertApproxEqAbs(pufferVault.totalSupply(), sharesBefore + sharesAmount, 1e9, "shares change"); - } - - // ETH and WETH and STETH deposits should give you the same amount of shares - function test_eth_weth_stETH_deposits() - public - giveToken(MAKER_VAULT, address(_WETH), alice, 100 ether) - giveToken(BLAST_DEPOSIT, address(stETH), alice, 100 ether) - withCaller(alice) - { - uint256 assetsBefore = pufferVault.totalAssets(); - uint256 sharesBefore = pufferVault.totalSupply(); - - // 10 ETH, 10 WETH, 10 stETH - uint256 depositAmount = 10 ether; - - _WETH.approve(address(pufferVault), type(uint256).max); - stETH.approve(address(pufferVault), type(uint256).max); - vm.deal(alice, 100 ether); - - uint256 stETHSharesAmount = _ST_ETH.getSharesByPooledEth(depositAmount); - - uint256 wethShares = pufferVault.deposit(depositAmount, alice); - uint256 stETHShares = pufferVault.depositStETH(stETHSharesAmount, alice); - uint256 ethShares = pufferVault.depositETH{ value: depositAmount }(alice); - - assertApproxEqAbs(wethShares, stETHShares, 1, "weth steth shares"); - assertApproxEqAbs(stETHShares, ethShares, 1, "eth steth shares"); - - assertApproxEqAbs(pufferVault.totalAssets(), assetsBefore + 3 * depositAmount, 1e9, "asset change"); - assertApproxEqAbs( - pufferVault.totalSupply(), sharesBefore + wethShares + stETHShares + ethShares, 1e9, "shares change" - ); - } - - // EL Deposits are Paused in the current block - // function test_el_stETH_deposit() public { - // uint256 exchangeRateBefore = pufferVault.previewDeposit(1 ether); - // vm.startPrank(OPERATIONS_MULTISIG); - // pufferVault.depositToEigenLayer(1000 ether); - // uint256 exchangeRateAfterDeposit = pufferVault.previewDeposit(1 ether); - // assertEq(exchangeRateBefore, exchangeRateAfterDeposit, "exchange rate must not change after the deposit to EL"); - // } - - function _withdraw_stETH_from_lido() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000 ether; // steth Amount - amounts[1] = 1000 ether; // steth Amount - - uint256 assetsBefore = pufferVault.totalAssets(); - uint256 sharesBefore = pufferVault.totalSupply(); - - vm.startPrank(OPERATIONS_MULTISIG); - uint256[] memory requestIds = pufferVault.initiateETHWithdrawalsFromLido(amounts); - - assertEq(pufferVault.getPendingLidoETHAmount(), 2000 ether); - - _finalizeWithdrawals(requestIds[1]); - - vm.roll(block.number + 10 days); - - // Claim withdrawals - pufferVault.claimWithdrawalsFromLido(requestIds); - - // Because we don't simulate an oracle update after we initiateETHWithdrawals, we get less than we sent. `976671819902367` less on 2k ETH - assertApproxEqAbs(pufferVault.totalAssets(), assetsBefore, 976671819902367, "asset change"); - assertApproxEqAbs(pufferVault.totalSupply(), sharesBefore, 1e9, "shares change"); - } -} diff --git a/mainnet-contracts/test/Integration/PufferVaultV2Sandwich.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2Sandwich.fork.t.sol deleted file mode 100644 index 02436650..00000000 --- a/mainnet-contracts/test/Integration/PufferVaultV2Sandwich.fork.t.sol +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; -import { IPufferVaultV2 } from "../../src/interface/IPufferVaultV2.sol"; - -contract PufferVaultV2SandwichTest is MainnetForkTestHelper { - address pufferWhale = 0xd164B614FdE7939078c7558F9680FA32f01aed77; - - function setUp() public virtual override { - // Cancun upgrade - vm.createSelectFork(vm.rpcUrl("mainnet"), 19504381); // ~ 2024-03-24 13:24:23) - - // Setup contracts that are deployed to mainnet - _setupLiveContracts(); - - // Upgrade to latest version - _upgradeToMainnetPuffer(); - } - - // Rebase increases Vault's totalAssets by +30~ eth - function test_rebase() public { - uint256 assetsBefore = pufferVault.totalAssets(); - - // Rebase lido is +30.7 ETH for the Vault - _rebaseLido(); - - uint256 assetsAfter = pufferVault.totalAssets(); - - assertGt(assetsAfter, assetsBefore, "assetsAfter > assetsBefore"); - assertEq(assetsAfter - assetsBefore, 30.747233933014735819 ether, "30 eth rebase"); - } - - // Attacker tries to use own capital to sandwich the Vault's withdrawal - // Sandwich attack can'e be in one transaction, it must be a MEV block - function test_sandwich_v2() public { - // Give ETH to the depositor(this contract) - vm.deal(address(this), 100 ether); - - // Give ETH to the PufferVault (withdrawal liqudiity) - vm.deal(address(pufferVault), 130 ether); - - // deposit 100 ETH - pufferVault.depositETH{ value: 100 ether }(address(this)); - - // Rebase lido is +30.7 ETH for the Vault - _rebaseLido(); - - // Withdraw - pufferVault.withdraw(pufferVault.maxWithdraw(address(this)), address(this), address(this)); - - // Attacker got less than 100 ETH - assertEq(_WETH.balanceOf(address(this)), 99.018071600759029089 ether, "~ 99 ether received"); - } - - // Even with fees 0, it is not really worth it to sandwich the Vault - // An attack with 99 ETH would only get 0.008 ETH profit (excluding gas fees) - // The attacked would need to have 100 ETH/WETH for this to be profitable and he would need to sandwich the Vault in a MEV block - function test_sandwich_v2_zero_withdrawal_fee() public { - // Set fees to 0 - // Timelock.sol is the admin of AccessManager - vm.startPrank(address(timelock)); - vm.expectEmit(true, true, true, true); - emit IPufferVaultV2.ExitFeeBasisPointsSet(100, 0); - pufferVault.setExitFeeBasisPoints(0); - vm.stopPrank(); - - // Give ETH to the depositor(this contract) - vm.deal(address(this), 99 ether); - - // Give ETH to the PufferVault (withdrawal liqudiity) - vm.deal(address(pufferVault), 130 ether); - - // deposit 100 ETH - pufferVault.depositETH{ value: 99 ether }(address(this)); - - // Rebase lido is +30.7 ETH for the Vault - _rebaseLido(); - - // Withdraw - pufferVault.withdraw(pufferVault.maxWithdraw(address(this)), address(this), address(this)); - - // Attacker got less than 100 ETH - assertEq(_WETH.balanceOf(address(this)), 99.008169815526098175 ether, "~ 99 ether received"); - // ~ 0.08 ETH profit doesn't seem worth the effort - } - - function _rebaseLido() internal { - // Simulates stETH rebasing by fast-forwarding block 19504382 where Lido oracle rebased. - // Submits the same call data as the Lido oracle. - // https://etherscan.io/tx/0x0a80282625c00aaa5b224011b35c3ac56783e62b2f7d55fc1550a0945245a8a7 - vm.roll(19504382); - vm.startPrank(0xc79F702202E3A6B0B6310B537E786B9ACAA19BAf); // Lido's whitelisted Oracle - (bool success,) = LIDO_ACCOUNTING_ORACLE.call( - hex"fc7377cd000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000084d31f000000000000000000000000000000000000000000000000000000000005252e0000000000000000000000000000000000000000000000000022abcbe40e8f6500000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000026d9c46441fbb977e00000000000000000000000000000000000000000000000006e51877e064ce1b1900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000003c0abf5096c484be3c46da300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001958bfceee23b63bd94cf905d77f457004f9972d768450eb9a9ae2d564adaf56c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000086ce00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000007985" - ); - assertTrue(success, "oracle rebase failed"); - vm.stopPrank(); - } -} diff --git a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol b/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol deleted file mode 100644 index c77aba46..00000000 --- a/mainnet-contracts/test/Integration/PufferVaultV2WithdrawFromEl.fork.t.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { Test } from "forge-std/Test.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; -import { IStETH } from "../../src/interface/Lido/IStETH.sol"; -import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IWETH } from "../../src/interface/Other/IWETH.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IEigenLayer } from "../../src/interface/Eigenlayer-Slashing/IEigenLayer.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; -import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; -import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; -import { PufferDeployment } from "../../src/structs/PufferDeployment.sol"; -import { DeployPufETH } from "script/DeployPufETH.s.sol"; -import { PufferDepositor } from "../../src/PufferDepositor.sol"; -import { PufferVault } from "../../src/PufferVault.sol"; -import { Timelock } from "../../src/Timelock.sol"; -import { GenerateAccessManagerCallData } from "script/GenerateAccessManagerCallData.sol"; -import { MockPufferOracle } from "../../test/mocks/MockPufferOracle.sol"; - -contract PufferVaultWithdrawalTest is Test { - PufferVaultV2 newImpl; - - PufferVault pufferVault; - AccessManager accessManager; - Timelock timelock; - IStETH stETH; - - address pufferDevWallet = 0xDDDeAfB492752FC64220ddB3E7C9f1d5CcCdFdF0; - address operations = 0x5568b309259131D3A7c128700195e0A1C94761A0; - address community = 0xf9F846FA49e79BE8d74c68CDC01AaaFfBBf8177F; - - address pufferDepositor; - - function setUp() public { - // Cancun upgrade - vm.createSelectFork(vm.rpcUrl("holesky"), 1304211); - - // Dep - PufferDeployment memory deployment = new DeployPufETH().run(); - pufferDepositor = deployment.pufferDepositor; - pufferVault = PufferVault(payable(deployment.pufferVault)); - accessManager = AccessManager(payable(deployment.accessManager)); - timelock = Timelock(payable(deployment.timelock)); - - stETH = IStETH(address(0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034)); - IWETH weth = IWETH(0xD6eF375Ad62f1d5BC06479fD0c7DCEF28e5Dc898); - ILidoWithdrawalQueue lidoWithdrawalQueue = ILidoWithdrawalQueue(0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50); - MockPufferOracle oracle = new MockPufferOracle(); - - newImpl = new PufferVaultV2(stETH, weth, lidoWithdrawalQueue, IPufferOracle(address(oracle))); - } - - // Update contracts and setup access - function _upgradeContracts() internal { - // Community multisig - vm.startPrank(community); - vm.expectEmit(true, true, true, true); - emit ERC1967Utils.Upgraded(address(newImpl)); - UUPSUpgradeable(pufferVault).upgradeToAndCall(address(newImpl), abi.encodeCall(PufferVaultV2.initialize, ())); - - // Setup access - bytes memory encodedMulticall = new GenerateAccessManagerCallData().run(address(pufferVault), pufferDepositor); - - (bool success,) = address(timelock).call( - abi.encodeWithSelector(Timelock.executeTransaction.selector, address(accessManager), encodedMulticall, 1) - ); - require(success, "failed upgrade tx"); - // Timelock is the owner of the AccessManager - // timelock.executeTransaction(address(accessManagerß), encodedMulticall, 1); - } -} diff --git a/mainnet-contracts/test/MainnetForkTestHelper.sol b/mainnet-contracts/test/MainnetForkTestHelper.sol index 0460d797..95b8d340 100644 --- a/mainnet-contracts/test/MainnetForkTestHelper.sol +++ b/mainnet-contracts/test/MainnetForkTestHelper.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.0 <0.9.0; import { Test } from "forge-std/Test.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; -import { PufferVaultV3 } from "../src/PufferVaultV3.sol"; import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { PufferVaultV5Tests } from "../test/mocks/PufferVaultV5Tests.sol"; import { PufferDepositorV2 } from "../src/PufferDepositorV2.sol"; @@ -53,7 +52,7 @@ contract MainnetForkTestHelper is Test, DeployerHelper { } PufferDepositorV2 public pufferDepositor; - PufferVaultV3 public pufferVault; + PufferVaultV5 public pufferVault; PufferVaultV5 public pufferVaultWithBlocking; // Non blocking version is required because of the foundry tests PufferVaultV5 public pufferVaultNonBlocking; @@ -118,7 +117,7 @@ contract MainnetForkTestHelper is Test, DeployerHelper { function _setupLiveContracts() internal { pufferDepositor = PufferDepositorV2(payable(0x4aA799C5dfc01ee7d790e3bf1a7C2257CE1DcefF)); - pufferVault = PufferVaultV3(payable(_getPufferVault())); + pufferVault = PufferVaultV5(payable(_getPufferVault())); accessManager = AccessManager(payable(_getAccessManager())); timelock = Timelock(payable(_getTimelock())); diff --git a/mainnet-contracts/test/fork-tests/PufferWithdrawalManager.fork.t.sol b/mainnet-contracts/test/fork-tests/PufferWithdrawalManager.fork.t.sol index 8a870c13..529df9ed 100644 --- a/mainnet-contracts/test/fork-tests/PufferWithdrawalManager.fork.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferWithdrawalManager.fork.t.sol @@ -9,7 +9,7 @@ import { IPufferWithdrawalManager } from "../../src/interface/IPufferWithdrawalM import { ValidatorTicket } from "../../src/ValidatorTicket.sol"; import { PufferWithdrawalManagerTests } from "../mocks/PufferWithdrawalManagerTests.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; -import { PufferVaultV3 } from "../../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; import { PufferOracle } from "../../src/PufferOracle.sol"; contract PufferWithdrawalManagerForkTest is MainnetForkTestHelper { @@ -74,7 +74,7 @@ contract PufferWithdrawalManagerForkTest is MainnetForkTestHelper { // Upgrade to the implementation that has the overridden `markWithdrawalRequest` modifier address newImpl = address( new PufferWithdrawalManagerTests( - batchSize, PufferVaultV3(payable(address(pufferVault))), IWETH(address(_WETH)) + batchSize, PufferVaultV5(payable(address(pufferVault))), IWETH(address(_WETH)) ) ); withdrawalManager.upgradeToAndCall(newImpl, ""); diff --git a/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol b/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol index 059aafbe..07f61145 100644 --- a/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol +++ b/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol @@ -6,11 +6,10 @@ import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; import { UpgradeValidatorTicket } from "../../script/UpgradeValidatorTicket.s.sol"; import { ValidatorTicket } from "../../src/ValidatorTicket.sol"; import { IValidatorTicket } from "../../src/interface/IValidatorTicket.sol"; -import { PufferVaultV3 } from "../../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; import { IPufferOracle } from "../../src/interface/IPufferOracle.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; contract ValidatorTicketMainnetTest is MainnetForkTestHelper { using Math for uint256; @@ -68,7 +67,7 @@ contract ValidatorTicketMainnetTest is MainnetForkTestHelper { uint256 vtPrice = IPufferOracle(address(validatorTicket.PUFFER_ORACLE())).getValidatorTicketPrice(); uint256 requiredETH = vtAmount.mulDiv(vtPrice, 1 ether, Math.Rounding.Ceil); uint256 expectedPufEthUsed = - PufferVaultV3(payable(validatorTicket.PUFFER_VAULT())).convertToSharesUp(requiredETH); + PufferVaultV5(payable(validatorTicket.PUFFER_VAULT())).convertToSharesUp(requiredETH); // Give whale some pufETH deal(address(validatorTicket.PUFFER_VAULT()), recipient, expectedPufEthUsed * 2); @@ -91,7 +90,7 @@ contract ValidatorTicketMainnetTest is MainnetForkTestHelper { uint256 vtPrice = IPufferOracle(address(validatorTicket.PUFFER_ORACLE())).getValidatorTicketPrice(); uint256 requiredETH = vtAmount.mulDiv(vtPrice, 1 ether, Math.Rounding.Ceil); - uint256 pufEthAmount = PufferVaultV3(payable(validatorTicket.PUFFER_VAULT())).convertToSharesUp(requiredETH); + uint256 pufEthAmount = PufferVaultV5(payable(validatorTicket.PUFFER_VAULT())).convertToSharesUp(requiredETH); deal(address(validatorTicket.PUFFER_VAULT()), recipient, pufEthAmount); @@ -184,7 +183,7 @@ contract ValidatorTicketMainnetTest is MainnetForkTestHelper { uint256 initialTreasuryBalance, uint256 initialGuardianBalance, uint256 initialVaultBalance - ) internal { + ) internal view { address treasury = validatorTicket.TREASURY(); address guardianModule = validatorTicket.GUARDIAN_MODULE(); address vault = validatorTicket.PUFFER_VAULT(); diff --git a/mainnet-contracts/test/handlers/PufferProtocolHandler.sol b/mainnet-contracts/test/handlers/PufferProtocolHandler.sol index 7d83dcd4..fe5a0c66 100644 --- a/mainnet-contracts/test/handlers/PufferProtocolHandler.sol +++ b/mainnet-contracts/test/handlers/PufferProtocolHandler.sol @@ -9,7 +9,6 @@ import { RaveEvidence } from "../../src/struct/RaveEvidence.sol"; import { console } from "forge-std/console.sol"; import { Test } from "forge-std/Test.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; import { stETHMock } from "../mocks/stETHMock.sol"; import { ValidatorKeyData } from "../../src/struct/ValidatorKeyData.sol"; import { Validator } from "../../src/struct/Validator.sol"; @@ -27,6 +26,7 @@ import { ValidatorTicket } from "../../src/ValidatorTicket.sol"; import { IValidatorTicket } from "../../src/interface/IValidatorTicket.sol"; import { PufferOracleV2 } from "../../src/PufferOracleV2.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; struct ProvisionedValidator { bytes32 moduleName; @@ -106,7 +106,7 @@ contract PufferProtocolHandler is Test { address internal currentActor; - PufferVaultV2 pufferVault; + PufferVaultV5 pufferVault; PufferOracleV2 pufferOracle; ValidatorTicket validatorTicket; @@ -114,7 +114,7 @@ contract PufferProtocolHandler is Test { constructor( UnitTestHelper helper, - PufferVaultV2 vault, + PufferVaultV5 vault, address steth, PufferProtocol protocol, uint256[] memory _guardiansEnclavePks, diff --git a/mainnet-contracts/test/helpers/UnitTestHelper.sol b/mainnet-contracts/test/helpers/UnitTestHelper.sol index 6442a7c6..a18338cc 100644 --- a/mainnet-contracts/test/helpers/UnitTestHelper.sol +++ b/mainnet-contracts/test/helpers/UnitTestHelper.sol @@ -19,8 +19,7 @@ import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessMana import { Permit } from "../../src/structs/Permit.sol"; import { PufferDepositor } from "../../src/PufferDepositor.sol"; import { PufferVault } from "../../src/PufferVault.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; -import { PufferVaultV3 } from "../../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; import { stETHMock } from "../mocks/stETHMock.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { ValidatorTicket } from "../../src/ValidatorTicket.sol"; @@ -86,7 +85,7 @@ contract UnitTestHelper is Test, BaseScript { hex"04a55b152177219971a93a64aafc2d61baeaf86526963caa260e71efa2b865527e0307d7bda85312dd6ff23bcc88f2bf228da6295239f72c31b686c48b7b69cdfd"; PufferDepositor public pufferDepositor; - PufferVaultV3 public pufferVault; + PufferVaultV5 public pufferVault; stETHMock public stETH; IWETH public weth; @@ -220,7 +219,7 @@ contract UnitTestHelper is Test, BaseScript { revenueDepositor = PufferRevenueDepositor(payable(pufferDeployment.revenueDepositor)); // pufETH dependencies - pufferVault = PufferVaultV3(payable(pufferDeployment.pufferVault)); + pufferVault = PufferVaultV5(payable(pufferDeployment.pufferVault)); pufferDepositor = PufferDepositor(payable(pufferDeployment.pufferDepositor)); stETH = stETHMock(payable(pufferDeployment.stETH)); weth = IWETH(payable(pufferDeployment.weth)); @@ -322,7 +321,7 @@ contract UnitTestHelper is Test, BaseScript { accessManager.grantRole(protocolRoleId, address(pufferProtocol), 0); bytes4[] memory selectors = new bytes4[](1); - selectors[0] = PufferVaultV2.transferETH.selector; + selectors[0] = PufferVaultV5.transferETH.selector; accessManager.setTargetFunctionRole(address(pufferVault), selectors, protocolRoleId); vm.stopPrank(); diff --git a/mainnet-contracts/test/mocks/PufferProtocolMockUpgrade.sol b/mainnet-contracts/test/mocks/PufferProtocolMockUpgrade.sol index 735f9287..94e11a2b 100644 --- a/mainnet-contracts/test/mocks/PufferProtocolMockUpgrade.sol +++ b/mainnet-contracts/test/mocks/PufferProtocolMockUpgrade.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { GuardianModule } from "../../src/GuardianModule.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; import { ValidatorTicket } from "../../src/ValidatorTicket.sol"; import { IPufferOracleV2 } from "../../src/interface/IPufferOracleV2.sol"; @@ -14,7 +14,7 @@ contract PufferProtocolMockUpgrade is PufferProtocol { constructor(address beacon) PufferProtocol( - PufferVaultV2(payable(address(0))), + PufferVaultV5(payable(address(0))), GuardianModule(payable(address(0))), address(0), ValidatorTicket(address(0)), diff --git a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol b/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol deleted file mode 100644 index fac2c48b..00000000 --- a/mainnet-contracts/test/mocks/PufferVaultV2Tests.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { PufferVaultV2 } from "src/PufferVaultV2.sol"; -import { IStETH } from "src/interface/Lido/IStETH.sol"; -import { ILidoWithdrawalQueue } from "src/interface/Lido/ILidoWithdrawalQueue.sol"; -import { IWETH } from "src/interface/Other/IWETH.sol"; -import { IPufferOracle } from "src/interface/IPufferOracle.sol"; - -contract PufferVaultV2Tests is PufferVaultV2 { - constructor(IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, IPufferOracle oracle) - PufferVaultV2(stETH, weth, lidoWithdrawalQueue, oracle) - { - _WETH = weth; - PUFFER_ORACLE = oracle; - _disableInitializers(); - } - - // This functionality must be disabled because of the foundry tests - modifier markDeposit() virtual override { - _; - } -} diff --git a/mainnet-contracts/test/mocks/PufferWithdrawalManagerTests.sol b/mainnet-contracts/test/mocks/PufferWithdrawalManagerTests.sol index 6d9d7be7..b923fc15 100644 --- a/mainnet-contracts/test/mocks/PufferWithdrawalManagerTests.sol +++ b/mainnet-contracts/test/mocks/PufferWithdrawalManagerTests.sol @@ -2,11 +2,11 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferWithdrawalManager } from "../../src/PufferWithdrawalManager.sol"; -import { PufferVaultV3 } from "../../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; contract PufferWithdrawalManagerTests is PufferWithdrawalManager { - constructor(uint256 batchSize, PufferVaultV3 pufferVault, IWETH weth) + constructor(uint256 batchSize, PufferVaultV5 pufferVault, IWETH weth) PufferWithdrawalManager(batchSize, pufferVault, weth) { } diff --git a/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol b/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol index e660faba..57cfdef7 100644 --- a/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol +++ b/mainnet-contracts/test/unit/PufferWithdrawalManager.t.sol @@ -11,7 +11,7 @@ import { Permit } from "../../src/structs/Permit.sol"; import { Generate2StepWithdrawalsCalldata } from "../../script/AccessManagerMigrations/04_Generate2StepWithdrawalsCalldata.s.sol"; import { PufferWithdrawalManagerTests } from "../mocks/PufferWithdrawalManagerTests.sol"; -import { PufferVaultV3 } from "../../src/PufferVaultV3.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; /** @@ -638,7 +638,7 @@ contract PufferWithdrawalManagerTest is UnitTestHelper { function testRevert_multipleWithdrawalsInTheSameTx() public withUnlimitedWithdrawalLimit { // Upgrade to the real implementation address newImpl = address( - new PufferWithdrawalManager(batchSize, PufferVaultV3(payable(address(pufferVault))), IWETH(address(weth))) + new PufferWithdrawalManager(batchSize, PufferVaultV5(payable(address(pufferVault))), IWETH(address(weth))) ); vm.prank(timelock); withdrawalManager.upgradeToAndCall(newImpl, ""); diff --git a/mainnet-contracts/test/unit/ValidatorTicket.t.sol b/mainnet-contracts/test/unit/ValidatorTicket.t.sol index 4f9447e7..c0dea532 100644 --- a/mainnet-contracts/test/unit/ValidatorTicket.t.sol +++ b/mainnet-contracts/test/unit/ValidatorTicket.t.sol @@ -8,11 +8,8 @@ import { ValidatorTicket } from "../../src/ValidatorTicket.sol"; import { IValidatorTicket } from "../../src/interface/IValidatorTicket.sol"; import { PufferOracle } from "../../src/PufferOracle.sol"; import { PufferOracleV2 } from "../../src/PufferOracleV2.sol"; -import { IPufferVault } from "../../src/interface/IPufferVault.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; -import { PUBLIC_ROLE, ROLE_ID_PUFETH_BURNER, ROLE_ID_VAULT_WITHDRAWER } from "../../script/Roles.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; +import { PUBLIC_ROLE, ROLE_ID_PUFETH_BURNER } from "../../script/Roles.sol"; import { Permit } from "../../src/structs/Permit.sol"; import "forge-std/console.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; @@ -44,7 +41,7 @@ contract ValidatorTicketTest is UnitTestHelper { console.log("validatorTicket", address(validatorTicket)); bytes4[] memory burnerSelectors = new bytes4[](1); - burnerSelectors[0] = PufferVaultV2.burn.selector; + burnerSelectors[0] = PufferVaultV5.burn.selector; accessManager.setTargetFunctionRole(address(pufferVault), burnerSelectors, ROLE_ID_PUFETH_BURNER); bytes4[] memory validatorTicketPublicSelectors = new bytes4[](3); diff --git a/mainnet-contracts/test/unit/ValidatorTicketPricer.t.sol b/mainnet-contracts/test/unit/ValidatorTicketPricer.t.sol index 01da5785..46879d9b 100644 --- a/mainnet-contracts/test/unit/ValidatorTicketPricer.t.sol +++ b/mainnet-contracts/test/unit/ValidatorTicketPricer.t.sol @@ -3,15 +3,12 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Script.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { UnitTestHelper } from "../helpers/UnitTestHelper.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { ValidatorTicket } from "../../src/ValidatorTicket.sol"; -import { IValidatorTicket } from "../../src/interface/IValidatorTicket.sol"; import { PufferOracle } from "../../src/PufferOracle.sol"; -import { PufferOracleV2 } from "../../src/PufferOracleV2.sol"; import { ValidatorTicketPricer } from "../../src/ValidatorTicketPricer.sol"; -import { ROLE_ID_OPERATIONS_PAYMASTER, ROLE_ID_OPERATIONS_MULTISIG, ROLE_ID_VT_PRICER } from "../../script/Roles.sol"; +import { ROLE_ID_OPERATIONS_PAYMASTER, ROLE_ID_VT_PRICER } from "../../script/Roles.sol"; /** * @dev This test is for the ValidatorTicket smart contract with `src/PufferOracle.sol` From 772a129b7639a37602e3b86f0737549af6fa854a Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 14:07:23 +0100 Subject: [PATCH 20/37] cleanup --- .../04_Generate2StepWithdrawalsCalldata.s.sol | 8 ++-- .../script/GenerateAccessManagerCallData.sol | 20 ++++---- mainnet-contracts/script/SetupAccess.s.sol | 6 +-- mainnet-contracts/script/UpgradePufETH.s.sol | 2 +- mainnet-contracts/src/PufferVaultStorage.sol | 4 +- mainnet-contracts/src/PufferVaultV5.sol | 48 ++++++++----------- ...{IPufferVaultV3.sol => IPufferVaultV5.sol} | 5 +- 7 files changed, 42 insertions(+), 51 deletions(-) rename mainnet-contracts/src/interface/{IPufferVaultV3.sol => IPufferVaultV5.sol} (90%) diff --git a/mainnet-contracts/script/AccessManagerMigrations/04_Generate2StepWithdrawalsCalldata.s.sol b/mainnet-contracts/script/AccessManagerMigrations/04_Generate2StepWithdrawalsCalldata.s.sol index 03bef82a..c1cc44aa 100644 --- a/mainnet-contracts/script/AccessManagerMigrations/04_Generate2StepWithdrawalsCalldata.s.sol +++ b/mainnet-contracts/script/AccessManagerMigrations/04_Generate2StepWithdrawalsCalldata.s.sol @@ -13,7 +13,7 @@ import { ROLE_ID_OPERATIONS_MULTISIG } from "../../script/Roles.sol"; import { PufferWithdrawalManager } from "../../src/PufferWithdrawalManager.sol"; -import { PufferVaultV2 } from "../../src/PufferVaultV2.sol"; +import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; contract Generate2StepWithdrawalsCalldata is Script { function run( @@ -65,7 +65,7 @@ contract Generate2StepWithdrawalsCalldata is Script { calldatas[7] = abi.encodeWithSelector(AccessManager.labelRole.selector, ROLE_ID_PUFETH_BURNER, "pufETH Burner"); bytes4[] memory vaultWithdrawerSelectors = new bytes4[](1); - vaultWithdrawerSelectors[0] = PufferVaultV2.transferETH.selector; + vaultWithdrawerSelectors[0] = PufferVaultV5.transferETH.selector; calldatas[8] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, @@ -75,7 +75,7 @@ contract Generate2StepWithdrawalsCalldata is Script { ); bytes4[] memory burnerSelectors = new bytes4[](1); - burnerSelectors[0] = PufferVaultV2.burn.selector; + burnerSelectors[0] = PufferVaultV5.burn.selector; calldatas[9] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, pufferVaultProxy, burnerSelectors, ROLE_ID_PUFETH_BURNER @@ -92,7 +92,7 @@ contract Generate2StepWithdrawalsCalldata is Script { // in AccessManager contract, one selector can be assigned to only one role for a target contract // see `AccessManager._setTargetFunctionRole` function - // creation of this new `ROLE_ID_VAULT_WITHDRAWER` and assigning the `PufferVaultV2.transferETH` selector to it + // creation of this new `ROLE_ID_VAULT_WITHDRAWER` and assigning the `PufferVaultV5.transferETH` selector to it // would revoke that ability from the original `ROLE_ID_PUFFER_PROTOCOL` // that's why we need to grant the `ROLE_ID_VAULT_WITHDRAWER` and `ROLE_ID_PUFETH_BURNER` to the pufferProtocolProxy calldatas[12] = diff --git a/mainnet-contracts/script/GenerateAccessManagerCallData.sol b/mainnet-contracts/script/GenerateAccessManagerCallData.sol index 15a31de5..3f5770e2 100644 --- a/mainnet-contracts/script/GenerateAccessManagerCallData.sol +++ b/mainnet-contracts/script/GenerateAccessManagerCallData.sol @@ -5,10 +5,10 @@ import { Script } from "forge-std/Script.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol"; import { console } from "forge-std/console.sol"; -import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { PufferDepositorV2 } from "../src/PufferDepositorV2.sol"; import { PufferDepositor } from "../src/PufferDepositor.sol"; -import { PUBLIC_ROLE, ROLE_ID_DAO, ROLE_ID_PUFFER_PROTOCOL, ROLE_ID_OPERATIONS_MULTISIG } from "./Roles.sol"; +import { PUBLIC_ROLE, ROLE_ID_PUFFER_PROTOCOL, ROLE_ID_OPERATIONS_MULTISIG } from "./Roles.sol"; /** * @title GenerateAccessManagerCallData @@ -40,10 +40,10 @@ contract GenerateAccessManagerCallData is Script { function _getPublicSelectorsCalldata(address pufferVaultProxy) internal pure returns (bytes memory) { // Public selectors for PufferVault bytes4[] memory publicSelectors = new bytes4[](4); - publicSelectors[0] = PufferVaultV2.withdraw.selector; - publicSelectors[1] = PufferVaultV2.redeem.selector; - publicSelectors[2] = PufferVaultV2.depositETH.selector; - publicSelectors[3] = PufferVaultV2.depositStETH.selector; + publicSelectors[0] = PufferVaultV5.withdraw.selector; + publicSelectors[1] = PufferVaultV5.redeem.selector; + publicSelectors[2] = PufferVaultV5.depositETH.selector; + publicSelectors[3] = PufferVaultV5.depositStETH.selector; // `deposit` and `mint` are already `restricted` and allowed for PUBLIC_ROLE (PufferVault deployment) return abi.encodeWithSelector( @@ -55,8 +55,8 @@ contract GenerateAccessManagerCallData is Script { // Puffer Protocol only // PufferProtocol will get `ROLE_ID_PUFFER_PROTOCOL` when it's deployed bytes4[] memory protocolSelectors = new bytes4[](2); - protocolSelectors[0] = PufferVaultV2.transferETH.selector; - protocolSelectors[1] = PufferVaultV2.burn.selector; + protocolSelectors[0] = PufferVaultV5.transferETH.selector; + protocolSelectors[1] = PufferVaultV5.burn.selector; return abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, pufferVaultProxy, protocolSelectors, ROLE_ID_PUFFER_PROTOCOL @@ -66,8 +66,8 @@ contract GenerateAccessManagerCallData is Script { function _getOperationsSelectorsCalldata(address pufferVaultProxy) internal pure returns (bytes memory) { // Operations multisig bytes4[] memory operationsSelectors = new bytes4[](2); - operationsSelectors[0] = PufferVaultV2.initiateETHWithdrawalsFromLido.selector; - operationsSelectors[1] = PufferVaultV2.claimWithdrawalsFromLido.selector; + operationsSelectors[0] = PufferVaultV5.initiateETHWithdrawalsFromLido.selector; + operationsSelectors[1] = PufferVaultV5.claimWithdrawalsFromLido.selector; return abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, diff --git a/mainnet-contracts/script/SetupAccess.s.sol b/mainnet-contracts/script/SetupAccess.s.sol index af4118d9..4ad40bea 100644 --- a/mainnet-contracts/script/SetupAccess.s.sol +++ b/mainnet-contracts/script/SetupAccess.s.sol @@ -14,7 +14,7 @@ import { EnclaveVerifier } from "../src/EnclaveVerifier.sol"; import { PufferOracleV2 } from "../src/PufferOracleV2.sol"; import { PufferProtocolDeployment } from "./DeploymentStructs.sol"; import { ValidatorTicket } from "../src/ValidatorTicket.sol"; -import { PufferVaultV2 } from "../src/PufferVaultV2.sol"; +import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { OperationsCoordinator } from "../src/OperationsCoordinator.sol"; import { ValidatorTicketPricer } from "../src/ValidatorTicketPricer.sol"; import { GenerateAccessManagerCallData } from "../script/GenerateAccessManagerCallData.sol"; @@ -57,7 +57,7 @@ contract SetupAccess is BaseScript { require(s, "failed setupAccess GenerateAccessManagerCallData 1"); // This will be executed by the operations multisig on mainnet - // PufferVaultV2 access setup + // PufferVaultV5 access setup bytes memory cd = new GenerateAccessManagerCallData().run(deployment.pufferVault, deployment.pufferDepositor); // console.logBytes(cd); (s,) = address(accessManager).call(cd); @@ -221,7 +221,7 @@ contract SetupAccess is BaseScript { bytes[] memory calldatas = new bytes[](1); bytes4[] memory protocolSelectors = new bytes4[](1); - protocolSelectors[0] = PufferVaultV2.transferETH.selector; + protocolSelectors[0] = PufferVaultV5.transferETH.selector; calldatas[0] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index adfe4b5d..a6e08107 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -15,7 +15,6 @@ import { LidoWithdrawalQueueMock } from "../test/mocks/LidoWithdrawalQueueMock.s import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { IWETH } from "../src/interface/Other/IWETH.sol"; import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { PufferDeployment } from "../src/structs/PufferDeployment.sol"; import { BridgingDeployment } from "./DeploymentStructs.sol"; @@ -67,6 +66,7 @@ contract UpgradePufETH is BaseScript { IPufferOracle(pufferOracle) ); + // It is necessary to upgrade to VaultV2 because in that upgrade we changed the underlying asset from stETH to WETH // Initialize VaultV2 to swap stETH for WETH as the asset UUPSUpgradeable(deployment.pufferVault).upgradeToAndCall( address(newImplementationV2), abi.encodeCall(PufferVaultV2.initialize, ()) diff --git a/mainnet-contracts/src/PufferVaultStorage.sol b/mainnet-contracts/src/PufferVaultStorage.sol index 5066976e..71c5166a 100644 --- a/mainnet-contracts/src/PufferVaultStorage.sol +++ b/mainnet-contracts/src/PufferVaultStorage.sol @@ -22,8 +22,8 @@ abstract contract PufferVaultStorage { // 6 Slots for Redemption logic uint256 lidoLockedETH; uint256 deprecated_eigenLayerPendingWithdrawalSharesAmount; // Not in use anymore - bool deprecated_isLidoWithdrawal; // Not in use in PufferVaultV2 - EnumerableSet.UintSet deprecated_lidoWithdrawals; // Not in use in PufferVaultV2 + bool deprecated_isLidoWithdrawal; // Not in use anymore + EnumerableSet.UintSet deprecated_lidoWithdrawals; // Not in use anymore EnumerableSet.Bytes32Set deprecated_eigenLayerWithdrawals; // Not in use anymore EnumerableMap.UintToUintMap lidoWithdrawalAmounts; // 1 Slot for daily withdrawal limits diff --git a/mainnet-contracts/src/PufferVaultV5.sol b/mainnet-contracts/src/PufferVaultV5.sol index 38897a5f..2ce5e556 100644 --- a/mainnet-contracts/src/PufferVaultV5.sol +++ b/mainnet-contracts/src/PufferVaultV5.sol @@ -18,18 +18,17 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import { IPufferVaultV3 } from "./interface/IPufferVaultV3.sol"; +import { IPufferVaultV5 } from "./interface/IPufferVaultV5.sol"; import { IPufferOracleV2 } from "./interface/IPufferOracleV2.sol"; -import { IPufferVaultV2 } from "./interface/IPufferVaultV2.sol"; import { IPufferRevenueDepositor } from "./interface/IPufferRevenueDepositor.sol"; /** * @title PufferVaultV5 - * @dev Implementation of the PufferVault version 5 contract. + * @dev Implementation of the PufferVault version 5. * @custom:security-contact security@puffer.fi */ contract PufferVaultV5 is - IPufferVaultV3, + IPufferVaultV5, IERC721Receiver, PufferVaultStorage, AccessManagedUpgradeable, @@ -43,30 +42,10 @@ contract PufferVaultV5 is using Math for uint256; uint256 private constant _BASIS_POINT_SCALE = 1e4; - - /** - * @dev stETH contract - */ IStETH internal immutable _ST_ETH; - - /** - * @dev Lido Withdrawal Queue - */ ILidoWithdrawalQueue internal immutable _LIDO_WITHDRAWAL_QUEUE; - - /** - * @dev The Wrapped Ethereum ERC20 token - */ IWETH internal immutable _WETH; - - /** - * @dev The PufferOracle contract - */ IPufferOracleV2 public immutable PUFFER_ORACLE; - - /** - * @notice The restaking rewards depositor contract. - */ IPufferRevenueDepositor public immutable RESTAKING_REWARDS_DEPOSITOR; constructor( @@ -84,10 +63,14 @@ contract PufferVaultV5 is _disableInitializers(); } + /** + * @notice Accept ETH from anywhere + */ receive() external payable virtual { } /** - * @inheritdoc IPufferVaultV3 + * @notice Returns the total reward mint amount. + * @return The total minted rewards amount. */ function getTotalRewardMintAmount() public view returns (uint256) { VaultStorage storage $ = _getPufferVaultStorage(); @@ -95,7 +78,8 @@ contract PufferVaultV5 is } /** - * @inheritdoc IPufferVaultV3 + * @notice Returns the total reward mint amount. + * @return The total deposited rewards amount. */ function getTotalRewardDepositAmount() public view returns (uint256) { VaultStorage storage $ = _getPufferVaultStorage(); @@ -254,7 +238,9 @@ contract PufferVaultV5 is } /** - * @inheritdoc IPufferVaultV2 + * @notice Deposits native ETH into the Puffer Vault + * @param receiver The recipient of pufETH tokens + * @return shares The amount of pufETH received from the deposit * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol */ function depositETH(address receiver) public payable virtual markDeposit restricted returns (uint256) { @@ -271,7 +257,11 @@ contract PufferVaultV5 is } /** - * @inheritdoc IPufferVaultV2 + * @notice Deposits stETH into the Puffer Vault + * @param stETHSharesAmount The shares amount of stETH to deposit + * @param receiver The recipient of pufETH tokens + * @return shares The amount of pufETH received from the deposit + * * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol */ function depositStETH(uint256 stETHSharesAmount, address receiver) @@ -493,7 +483,7 @@ contract PufferVaultV5 is } /** - * @inheritdoc IPufferVaultV2 + * @notice Returns the current exit fee basis points */ function getExitFeeBasisPoints() public view virtual returns (uint256) { VaultStorage storage $ = _getPufferVaultStorage(); diff --git a/mainnet-contracts/src/interface/IPufferVaultV3.sol b/mainnet-contracts/src/interface/IPufferVaultV5.sol similarity index 90% rename from mainnet-contracts/src/interface/IPufferVaultV3.sol rename to mainnet-contracts/src/interface/IPufferVaultV5.sol index 8430fa0e..a5c9f486 100644 --- a/mainnet-contracts/src/interface/IPufferVaultV3.sol +++ b/mainnet-contracts/src/interface/IPufferVaultV5.sol @@ -4,11 +4,12 @@ pragma solidity >=0.8.0 <0.9.0; import { IPufferVaultV2 } from "./IPufferVaultV2.sol"; /** - * @title IPufferVaultV3 + * @title IPufferVaultV5 * @notice Interface for the PufferVault version 3 contract. + * @dev Interface V3 did not contain any new public functions. * @custom:security-contact security@puffer.fi */ -interface IPufferVaultV3 is IPufferVaultV2 { +interface IPufferVaultV5 is IPufferVaultV2 { /** * @notice Constructor parameters for bridging. * @param xToken The address of the xToken contract. From 1e738b2c26ba1086245b6701e20215aa3cf1bc7b Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 14:27:34 +0100 Subject: [PATCH 21/37] refactor vault v5, minor gas optimisation --- mainnet-contracts/src/PufferVaultV5.sol | 362 ++++++++++++------------ 1 file changed, 180 insertions(+), 182 deletions(-) diff --git a/mainnet-contracts/src/PufferVaultV5.sol b/mainnet-contracts/src/PufferVaultV5.sol index 2ce5e556..cbdef8d7 100644 --- a/mainnet-contracts/src/PufferVaultV5.sol +++ b/mainnet-contracts/src/PufferVaultV5.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.0 <0.9.0; import { PufferVaultStorage } from "./PufferVaultStorage.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { AccessManagedUpgradeable } from "@openzeppelin-contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; @@ -17,7 +16,6 @@ import { IWETH } from "./interface/Other/IWETH.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; -import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { IPufferVaultV5 } from "./interface/IPufferVaultV5.sol"; import { IPufferOracleV2 } from "./interface/IPufferOracleV2.sol"; import { IPufferRevenueDepositor } from "./interface/IPufferRevenueDepositor.sol"; @@ -38,7 +36,6 @@ contract PufferVaultV5 is { using SafeERC20 for address; using EnumerableMap for EnumerableMap.UintToUintMap; - using EnumerableSet for EnumerableSet.Bytes32Set; using Math for uint256; uint256 private constant _BASIS_POINT_SCALE = 1e4; @@ -68,26 +65,124 @@ contract PufferVaultV5 is */ receive() external payable virtual { } + modifier markDeposit() virtual { + //solhint-disable-next-line no-inline-assembly + assembly { + tstore(_DEPOSIT_TRACKER_LOCATION, 1) // Store `1` in the deposit tracker location + } + _; + } + + modifier revertIfDeposited() virtual { + //solhint-disable-next-line no-inline-assembly + assembly { + // If the deposit tracker location is set to `1`, revert with `DepositAndWithdrawalForbidden()` + if tload(_DEPOSIT_TRACKER_LOCATION) { + mstore(0x00, 0x39b79d11) // Store the error signature `0x39b79d11` for `error DepositAndWithdrawalForbidden()` in memory. + revert(0x1c, 0x04) // Revert by returning those 4 bytes. `revert DepositAndWithdrawalForbidden()` + } + } + _; + } + /** - * @notice Returns the total reward mint amount. - * @return The total minted rewards amount. + * @dev See {IERC4626-totalAssets}. + * pufETH, the shares of the vault, will be backed primarily by the WETH asset. + * However, at any point in time, the full backings may be a combination of stETH, WETH, and ETH. + * `totalAssets()` is calculated by summing the following: + * + WETH held in the vault contract + * + ETH held in the vault contract + * + PUFFER_ORACLE.getLockedEthAmount(), which is the oracle-reported Puffer validator ETH locked in the Beacon chain + * + getTotalRewardMintAmount(), which is the total amount of rewards minted + * - getTotalRewardDepositAmount(), which is the total amount of rewards deposited to the Vault + * - RESTAKING_REWARDS_DEPOSITOR.getPendingDistributionAmount(), which is the total amount of rewards pending distribution + * - callvalue(), which is the amount of ETH deposited by the caller in the transaction, it will be non zero only when the caller is depositing ETH + * + * NOTE on the native ETH deposits: + * When dealing with NATIVE ETH deposits, we need to deduct callvalue from the balance. + * The contract calculates the amount of shares(pufETH) to mint based on the total assets. + * When a user sends ETH, the msg.value is immediately added to address(this).balance. + * Since address(this.balance)` is used in calculating `totalAssets()`, we must deduct the `callvalue()` from the balance to prevent the user from minting excess shares. + * `msg.value` cannot be accessed from a view function, so we use assembly to get the callvalue. */ - function getTotalRewardMintAmount() public view returns (uint256) { - VaultStorage storage $ = _getPufferVaultStorage(); - return $.totalRewardMintAmount; + function totalAssets() public view virtual override returns (uint256) { + uint256 callValue; + // solhint-disable-next-line no-inline-assembly + assembly { + callValue := callvalue() + } + return _ST_ETH.balanceOf(address(this)) + getPendingLidoETHAmount() + _WETH.balanceOf(address(this)) + + (address(this).balance - callValue) + PUFFER_ORACLE.getLockedEthAmount() + getTotalRewardMintAmount() + - getTotalRewardDepositAmount() - RESTAKING_REWARDS_DEPOSITOR.getPendingDistributionAmount(); } /** - * @notice Returns the total reward mint amount. - * @return The total deposited rewards amount. + * @notice Deposits native ETH into the Puffer Vault + * @param receiver The recipient of pufETH tokens + * @return shares The amount of pufETH received from the deposit + * + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol */ - function getTotalRewardDepositAmount() public view returns (uint256) { - VaultStorage storage $ = _getPufferVaultStorage(); - return $.totalRewardDepositAmount; + function depositETH(address receiver) public payable virtual markDeposit restricted returns (uint256) { + uint256 shares = previewDeposit(msg.value); + _mint(receiver, shares); + emit Deposit(_msgSender(), receiver, msg.value, shares); + + return shares; + } + + /** + * @notice Deposits stETH into the Puffer Vault + * @param stETHSharesAmount The shares amount of stETH to deposit + * @param receiver The recipient of pufETH tokens + * @return shares The amount of pufETH received from the deposit + * + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + */ + function depositStETH(uint256 stETHSharesAmount, address receiver) + public + virtual + markDeposit + restricted + returns (uint256) + { + // Get the amount of assets (stETH) that corresponds to `stETHSharesAmount` so that we can use it in our calculation + uint256 assets = _ST_ETH.getPooledEthByShares(stETHSharesAmount); + + uint256 shares = previewDeposit(assets); + // Transfer the exact number of stETH shares from the user to the vault + _ST_ETH.transferSharesFrom({ _sender: msg.sender, _recipient: address(this), _sharesAmount: stETHSharesAmount }); + _mint(receiver, shares); + + emit Deposit(_msgSender(), receiver, assets, shares); + + return shares; + } + + /** + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + */ + function deposit(uint256 assets, address receiver) + public + virtual + override + markDeposit + restricted + returns (uint256) + { + return super.deposit(assets, receiver); + } + + /** + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol + */ + function mint(uint256 shares, address receiver) public virtual override markDeposit restricted returns (uint256) { + return super.mint(shares, receiver); } /** * @notice Mints pufETH rewards for the L1RewardManager contract and returns the exchange rate. + * * @dev Restricted to L1RewardManager */ function mintRewards(uint256 rewardsAmount) @@ -115,6 +210,7 @@ contract PufferVaultV5 is /** * @notice Deposits the rewards amount to the vault and updates the total reward deposit amount. + * * @dev Restricted to PufferModuleManager */ function depositRewards() external payable restricted { @@ -128,6 +224,7 @@ contract PufferVaultV5 is /** * @notice Reverts the `mintRewards` action. + * * @dev Restricted to L1RewardManager */ function revertMintRewards(uint256 pufETHAmount, uint256 ethAmount) external restricted { @@ -144,45 +241,16 @@ contract PufferVaultV5 is _burn(msg.sender, pufETHAmount); } - /** - * @dev See {IERC4626-totalAssets}. - * pufETH, the shares of the vault, will be backed primarily by the WETH asset. - * However, at any point in time, the full backings may be a combination of stETH, WETH, and ETH. - * `totalAssets()` is calculated by summing the following: - * - WETH held in the vault contract - * - ETH held in the vault contract - * - PUFFER_ORACLE.getLockedEthAmount(), which is the oracle-reported Puffer validator ETH locked in the Beacon chain - * - getTotalRewardMintAmount(), which is the total amount of rewards minted - * - getTotalRewardDepositAmount(), which is the total amount of rewards deposited to the Vault - * - RESTAKING_REWARDS_DEPOSITOR.getPendingDistributionAmount(), which is the total amount of rewards pending distribution - * - * NOTE on the native ETH deposits: - * When dealing with NATIVE ETH deposits, we need to deduct callvalue from the balance. - * The contract calculates the amount of shares(pufETH) to mint based on the total assets. - * When a user sends ETH, the msg.value is immediately added to address(this).balance. - * Since address(this.balance)` is used in calculating `totalAssets()`, we must deduct the `callvalue()` from the balance to prevent the user from minting excess shares. - * `msg.value` cannot be accessed from a view function, so we use assembly to get the callvalue. - */ - function totalAssets() public view virtual override returns (uint256) { - uint256 callValue; - // solhint-disable-next-line no-inline-assembly - assembly { - callValue := callvalue() - } - return _ST_ETH.balanceOf(address(this)) + getPendingLidoETHAmount() + _WETH.balanceOf(address(this)) - + (address(this).balance - callValue) + PUFFER_ORACLE.getLockedEthAmount() + getTotalRewardMintAmount() - - getTotalRewardDepositAmount() - RESTAKING_REWARDS_DEPOSITOR.getPendingDistributionAmount(); - } - /** * @notice Withdrawals WETH assets from the vault, burning the `owner`'s (pufETH) shares. * The caller of this function does not have to be the `owner` if the `owner` has approved the caller to spend their pufETH. - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol * Copied the original ERC4626 code back to override `PufferVault` + wrap ETH logic * @param assets The amount of assets (WETH) to withdraw * @param receiver The address to receive the assets (WETH) * @param owner The address of the owner for which the shares (pufETH) are burned. * @return shares The amount of shares (pufETH) burned + * + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol */ function withdraw(uint256 assets, address receiver, address owner) public @@ -208,12 +276,13 @@ contract PufferVaultV5 is /** * @notice Redeems (pufETH) `shares` to receive (WETH) assets from the vault, burning the `owner`'s (pufETH) `shares`. * The caller of this function does not have to be the `owner` if the `owner` has approved the caller to spend their pufETH. - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol * Copied the original ERC4626 code back to override `PufferVault` + wrap ETH logic * @param shares The amount of shares (pufETH) to withdraw * @param receiver The address to receive the assets (WETH) * @param owner The address of the owner for which the shares (pufETH) are burned. * @return assets The amount of assets (WETH) redeemed + * + * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol */ function redeem(uint256 shares, address receiver, address owner) public @@ -237,85 +306,12 @@ contract PufferVaultV5 is return assets; } - /** - * @notice Deposits native ETH into the Puffer Vault - * @param receiver The recipient of pufETH tokens - * @return shares The amount of pufETH received from the deposit - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol - */ - function depositETH(address receiver) public payable virtual markDeposit restricted returns (uint256) { - uint256 maxAssets = maxDeposit(receiver); - if (msg.value > maxAssets) { - revert ERC4626ExceededMaxDeposit(receiver, msg.value, maxAssets); - } - - uint256 shares = previewDeposit(msg.value); - _mint(receiver, shares); - emit Deposit(_msgSender(), receiver, msg.value, shares); - - return shares; - } - - /** - * @notice Deposits stETH into the Puffer Vault - * @param stETHSharesAmount The shares amount of stETH to deposit - * @param receiver The recipient of pufETH tokens - * @return shares The amount of pufETH received from the deposit - * - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol - */ - function depositStETH(uint256 stETHSharesAmount, address receiver) - public - virtual - markDeposit - restricted - returns (uint256) - { - uint256 maxAssets = maxDeposit(receiver); - - // Get the amount of assets (stETH) that corresponds to `stETHSharesAmount` so that we can use it in our calculation - uint256 assets = _ST_ETH.getPooledEthByShares(stETHSharesAmount); - - if (assets > maxAssets) { - revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets); - } - - uint256 shares = previewDeposit(assets); - // Transfer the exact number of stETH shares from the user to the vault - _ST_ETH.transferSharesFrom({ _sender: msg.sender, _recipient: address(this), _sharesAmount: stETHSharesAmount }); - _mint(receiver, shares); - - emit Deposit(_msgSender(), receiver, assets, shares); - - return shares; - } - - /** - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol - */ - function deposit(uint256 assets, address receiver) - public - virtual - override - markDeposit - restricted - returns (uint256) - { - return super.deposit(assets, receiver); - } - - /** - * @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol - */ - function mint(uint256 shares, address receiver) public virtual override markDeposit restricted returns (uint256) { - return super.mint(shares, receiver); - } - /** * @notice Initiates ETH withdrawals from Lido - * @dev Restricted to Operations Multisig * @param amounts An array of stETH amounts to queue * @return requestIds An array of request IDs for the withdrawals + * + * @dev Restricted to Operations Multisig */ function initiateETHWithdrawalsFromLido(uint256[] calldata amounts) external @@ -345,8 +341,9 @@ contract PufferVaultV5 is /** * @notice Claims ETH withdrawals from Lido - * @dev Restricted to Operations Multisig * @param requestIds An array of request IDs for the withdrawals + * + * @dev Restricted to Operations Multisig */ function claimWithdrawalsFromLido(uint256[] calldata requestIds) external virtual restricted { require(requestIds.length != 0); @@ -379,10 +376,11 @@ contract PufferVaultV5 is /** * @notice Transfers ETH to a specified address. - * @dev Restricted to PufferProtocol smart contract * @dev It is used to transfer ETH to PufferModules to fund Puffer validators. * @param to The address of the PufferModule to transfer ETH to * @param ethAmount The amount of ETH to transfer + * + * @dev Restricted to PufferProtocol|PufferWithdrawalManager smart contracts */ function transferETH(address to, uint256 ethAmount) external restricted { // Our Vault holds ETH & WETH @@ -404,61 +402,20 @@ contract PufferVaultV5 is emit TransferredETH(to, ethAmount); } - /** - * @notice Returns the amount of ETH that is pending withdrawal from Lido - * @return The amount of ETH pending withdrawal - */ - function getPendingLidoETHAmount() public view virtual returns (uint256) { - VaultStorage storage $ = _getPufferVaultStorage(); - return $.lidoLockedETH; - } - - /** - * @notice Required by the ERC721 Standard to receive Lido withdrawal NFT - */ - function onERC721Received(address, address, uint256, bytes calldata) external virtual returns (bytes4) { - return IERC721Receiver.onERC721Received.selector; - } - - /** - * @notice Returns the number of decimals used to get its user representation. - */ - function decimals() public pure override(ERC20Upgradeable, ERC4626Upgradeable) returns (uint8) { - return 18; - } - - /** - * @notice Wraps the vault's ETH balance to WETH. - * @dev Used to provide WETH liquidity - */ - function _wrapETH(uint256 assets) internal virtual { - uint256 wethBalance = _WETH.balanceOf(address(this)); - - if (wethBalance < assets) { - _WETH.deposit{ value: assets - wethBalance }(); - } - } - /** * @notice Allows the `msg.sender` to burn their (pufETH) shares - * @dev Restricted to PufferProtocol * It is used to burn portions of Puffer validator bonds due to inactivity or slashing * @param shares The amount of shares to burn + * + * @dev Restricted to PufferProtocol|PufferWithdrawalManager smart contracts */ function burn(uint256 shares) public restricted { _burn(msg.sender, shares); } - /** - * @notice Returns the amount of shares (pufETH) for the `assets` amount rounded up - * @param assets The amount of assets - */ - function convertToSharesUp(uint256 assets) public view returns (uint256) { - return _convertToShares(assets, Math.Rounding.Ceil); - } - /** * @param newExitFeeBasisPoints is the new exit fee basis points + * * @dev Restricted to the DAO */ function setExitFeeBasisPoints(uint256 newExitFeeBasisPoints) external restricted { @@ -490,6 +447,55 @@ contract PufferVaultV5 is return $.exitFeeBasisPoints; } + /** + * @notice Returns the amount of ETH that is pending withdrawal from Lido + * @return The amount of ETH pending withdrawal + */ + function getPendingLidoETHAmount() public view virtual returns (uint256) { + VaultStorage storage $ = _getPufferVaultStorage(); + return $.lidoLockedETH; + } + + /** + * @notice Returns the total reward mint amount + * @return The total minted rewards amount + */ + function getTotalRewardMintAmount() public view returns (uint256) { + VaultStorage storage $ = _getPufferVaultStorage(); + return $.totalRewardMintAmount; + } + + /** + * @notice Returns the total reward deposit amount + * @return The total deposited rewards amount + */ + function getTotalRewardDepositAmount() public view returns (uint256) { + VaultStorage storage $ = _getPufferVaultStorage(); + return $.totalRewardDepositAmount; + } + + /** + * @notice Returns the amount of shares (pufETH) for the `assets` amount rounded up + * @param assets The amount of assets + */ + function convertToSharesUp(uint256 assets) public view returns (uint256) { + return _convertToShares(assets, Math.Rounding.Ceil); + } + + /** + * @notice Required by the ERC721 Standard to receive Lido withdrawal NFT + */ + function onERC721Received(address, address, uint256, bytes calldata) external virtual returns (bytes4) { + return IERC721Receiver.onERC721Received.selector; + } + + /** + * @notice Returns the number of decimals used to get its user representation + */ + function decimals() public pure override(ERC20Upgradeable, ERC4626Upgradeable) returns (uint8) { + return 18; + } + /** * @dev Calculates the fees that should be added to an amount `assets` that does not already include fees. * Used in {IERC4626-withdraw}. @@ -520,24 +526,16 @@ contract PufferVaultV5 is $.exitFeeBasisPoints = newExitFeeBasisPoints; } - modifier markDeposit() virtual { - //solhint-disable-next-line no-inline-assembly - assembly { - tstore(_DEPOSIT_TRACKER_LOCATION, 1) // Store `1` in the deposit tracker location - } - _; - } + /** + * @notice Wraps the vault's ETH balance to WETH + * @dev Used to provide WETH liquidity + */ + function _wrapETH(uint256 assets) internal virtual { + uint256 wethBalance = _WETH.balanceOf(address(this)); - modifier revertIfDeposited() virtual { - //solhint-disable-next-line no-inline-assembly - assembly { - // If the deposit tracker location is set to `1`, revert with `DepositAndWithdrawalForbidden()` - if tload(_DEPOSIT_TRACKER_LOCATION) { - mstore(0x00, 0x39b79d11) // Store the error signature `0x39b79d11` for `error DepositAndWithdrawalForbidden()` in memory. - revert(0x1c, 0x04) // Revert by returning those 4 bytes. `revert DepositAndWithdrawalForbidden()` - } + if (wethBalance < assets) { + _WETH.deposit{ value: assets - wethBalance }(); } - _; } /** From 4a9f219a9347b96e045bf9ae313c373c5e382172 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 14:59:54 +0100 Subject: [PATCH 22/37] remove unused scripts, remove dead code --- .../script/DeployBSCXpufETH.s.sol | 108 ------------------ .../script/DeployEverything.s.sol | 2 +- .../script/DeployPufETHBridging.s.sol | 1 - .../script/DeployPufferModuleManager.s.sol | 2 +- mainnet-contracts/script/DeploySOON.s.sol | 25 ---- mainnet-contracts/script/UpgradePufETH.s.sol | 7 +- .../test/unit/ValidatorTicket.t.sol | 5 - 7 files changed, 3 insertions(+), 147 deletions(-) delete mode 100644 mainnet-contracts/script/DeployBSCXpufETH.s.sol delete mode 100644 mainnet-contracts/script/DeploySOON.s.sol diff --git a/mainnet-contracts/script/DeployBSCXpufETH.s.sol b/mainnet-contracts/script/DeployBSCXpufETH.s.sol deleted file mode 100644 index 8d11a8c6..00000000 --- a/mainnet-contracts/script/DeployBSCXpufETH.s.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import "forge-std/Script.sol"; -import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; -import { stdJson } from "forge-std/StdJson.sol"; -import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol"; -import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { xPufETH } from "src/l2/xPufETH.sol"; -import { Timelock } from "../src/Timelock.sol"; -import { BaseScript } from "script/BaseScript.s.sol"; - -import { ROLE_ID_OPERATIONS_MULTISIG, ROLE_ID_DAO, PUBLIC_ROLE } from "./Roles.sol"; - -/** - * // Check that the simulation - * add --slow if deploying to a mainnet fork like tenderly (its buggy sometimes) - * - * forge script script/DeployBSCXpufETH.s.sol:DeployBSCXpufETH --rpc-url $RPC_URL --account puffer - * - * forge cache clean - * - * forge script script/DeployBSCXpufETH.s.sol:DeployBSCXpufETH --rpc-url $RPC_URL --account puffer --broadcast - */ -contract DeployBSCXpufETH is BaseScript { - address OPERATIONS_MULTISIG = 0xc1c5bb23f6D06fb1Aa9208BE59400874D74A78AD; - address COMMUNITY_MULTISIG = 0xA654561EEAcbCa8d044ce38Cf78ae29AEee032CB; - address PAUSER_MULTISIG = 0x53974Be9B9Bb363F5459281D4e70D4eF32F5e28B; - - // https://docs.connext.network/resources/deployments - address CONNEXT_BRIDGE = 0xCd401c10afa37d641d2F594852DA94C700e4F2CE; - - uint256 MINTING_LIMIT = 100 ether; - uint256 BURNING_LIMIT = 100 ether; - - Timelock timelock; - AccessManager accessManager; - - xPufETH public xPufETHProxy; - - function run() public broadcast { - accessManager = new AccessManager(_broadcaster); - timelock = new Timelock({ - accessManager: address(accessManager), - communityMultisig: COMMUNITY_MULTISIG, - operationsMultisig: OPERATIONS_MULTISIG, - pauser: PAUSER_MULTISIG, - initialDelay: 7 days - }); - - xPufETH xpufETHImplementation = new xPufETH(); - - xPufETHProxy = xPufETH( - address( - new ERC1967Proxy{ salt: bytes32("xPufETH") }( - address(xpufETHImplementation), abi.encodeCall(xPufETH.initialize, (address(accessManager))) - ) - ) - ); - console.log("Timelock:", address(timelock)); - console.log("AccessManager:", address(accessManager)); - console.log("xpufETHProxy:", address(xPufETHProxy)); - console.log("xpufETH implementation:", address(xpufETHImplementation)); - - // setup the limits for the bridge - bytes memory setLimitsCalldata = - abi.encodeWithSelector(xPufETH.setLimits.selector, CONNEXT_BRIDGE, MINTING_LIMIT, BURNING_LIMIT); - accessManager.execute(address(xPufETHProxy), setLimitsCalldata); - - // setup all access manager roles - bytes[] memory calldatas = _generateAccessManagerCallData(); - accessManager.multicall(calldatas); - } - - function _generateAccessManagerCallData() internal view returns (bytes[] memory) { - bytes[] memory calldatas = new bytes[](6); - - calldatas[0] = abi.encodeWithSelector(AccessManager.grantRole.selector, ROLE_ID_DAO, OPERATIONS_MULTISIG, 0); - - calldatas[1] = abi.encodeWithSelector( - AccessManager.grantRole.selector, ROLE_ID_OPERATIONS_MULTISIG, OPERATIONS_MULTISIG, 0 - ); - - bytes4[] memory daoSelectors = new bytes4[](2); - daoSelectors[0] = xPufETH.setLockbox.selector; - daoSelectors[1] = xPufETH.setLimits.selector; - - calldatas[2] = abi.encodeWithSelector( - AccessManager.setTargetFunctionRole.selector, address(xPufETHProxy), daoSelectors, ROLE_ID_DAO - ); - - bytes4[] memory publicSelectors = new bytes4[](2); - publicSelectors[0] = xPufETH.mint.selector; - publicSelectors[1] = xPufETH.burn.selector; - - calldatas[3] = abi.encodeWithSelector( - AccessManager.setTargetFunctionRole.selector, address(xPufETHProxy), publicSelectors, PUBLIC_ROLE - ); - - calldatas[4] = - abi.encodeWithSelector(AccessManager.grantRole.selector, accessManager.ADMIN_ROLE(), address(timelock), 0); - - calldatas[5] = - abi.encodeWithSelector(AccessManager.revokeRole.selector, accessManager.ADMIN_ROLE(), _broadcaster); - - return calldatas; - } -} diff --git a/mainnet-contracts/script/DeployEverything.s.sol b/mainnet-contracts/script/DeployEverything.s.sol index 8572dd02..a32ea88e 100644 --- a/mainnet-contracts/script/DeployEverything.s.sol +++ b/mainnet-contracts/script/DeployEverything.s.sol @@ -64,7 +64,7 @@ contract DeployEverything is BaseScript { address revenueDepositor = _deployRevenueDepositor(puffETHDeployment); pufferDeployment.revenueDepositor = revenueDepositor; - new UpgradePufETH().run(puffETHDeployment, bridgingDeployment, pufferOracle, revenueDepositor); + new UpgradePufETH().run(puffETHDeployment, pufferOracle, revenueDepositor); // `anvil` in the terminal if (_localAnvil) { diff --git a/mainnet-contracts/script/DeployPufETHBridging.s.sol b/mainnet-contracts/script/DeployPufETHBridging.s.sol index 85112410..623b1824 100644 --- a/mainnet-contracts/script/DeployPufETHBridging.s.sol +++ b/mainnet-contracts/script/DeployPufETHBridging.s.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/Script.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { BaseScript } from ".//BaseScript.s.sol"; -import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { PufferDeployment } from "../src/structs/PufferDeployment.sol"; import { BridgingDeployment } from "./DeploymentStructs.sol"; diff --git a/mainnet-contracts/script/DeployPufferModuleManager.s.sol b/mainnet-contracts/script/DeployPufferModuleManager.s.sol index e2a099ca..7b9ea6eb 100644 --- a/mainnet-contracts/script/DeployPufferModuleManager.s.sol +++ b/mainnet-contracts/script/DeployPufferModuleManager.s.sol @@ -8,10 +8,10 @@ import { BaseScript } from "script/BaseScript.s.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { PufferModuleManager } from "../src/PufferModuleManager.sol"; import { DeployerHelper } from "./DeployerHelper.s.sol"; + /** * forge script script/DeployPufferModuleManager.s.sol:DeployPufferModuleManager -vvvv --rpc-url=$RPC_URL --broadcast --verify */ - contract DeployPufferModuleManager is DeployerHelper { function run() public { vm.startBroadcast(); diff --git a/mainnet-contracts/script/DeploySOON.s.sol b/mainnet-contracts/script/DeploySOON.s.sol deleted file mode 100644 index f13afbf1..00000000 --- a/mainnet-contracts/script/DeploySOON.s.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import "forge-std/Script.sol"; -import { DeployerHelper } from "./DeployerHelper.s.sol"; -import { SOON } from "../src/SOON.sol"; - -/** - * forge script script/DeploySOON.s.sol:DeploySOON --rpc-url=$RPC_URL --private-key $PK - * - * deploy along with verification: - * forge script script/DeploySOON.s.sol:DeploySOON -vvvv --rpc-url=$RPC_URL --account puffer --verify --etherscan-api-key $ETHERSCAN_API_KEY --broadcast - */ -contract DeploySOON is DeployerHelper { - function run() public { - vm.startBroadcast(); - address multiSig = 0xE06A1ad7346Dfda7Ce9BCFba751DABFd754BAfAD; - - SOON soon = new SOON(multiSig); - - vm.label(address(soon), "SOON"); - - vm.stopBroadcast(); - } -} diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index a6e08107..972523fd 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -50,12 +50,7 @@ contract UpgradePufETH is BaseScript { ILidoWithdrawalQueue internal constant _LIDO_WITHDRAWAL_QUEUE = ILidoWithdrawalQueue(0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1); - function run( - PufferDeployment memory deployment, - BridgingDeployment memory bridgingDeployment, - address pufferOracle, - address revenueDepositor - ) public broadcast { + function run(PufferDeployment memory deployment, address pufferOracle, address revenueDepositor) public broadcast { //@todo this is for tests only AccessManager(deployment.accessManager).grantRole(1, _broadcaster, 0); diff --git a/mainnet-contracts/test/unit/ValidatorTicket.t.sol b/mainnet-contracts/test/unit/ValidatorTicket.t.sol index c0dea532..72d55e03 100644 --- a/mainnet-contracts/test/unit/ValidatorTicket.t.sol +++ b/mainnet-contracts/test/unit/ValidatorTicket.t.sol @@ -240,11 +240,6 @@ contract ValidatorTicketTest is UnitTestHelper { deal(address(pufferVault), recipient, pufEthAmount); } - function _signPermit(bytes32 structHash, bytes32 domainSeparator) internal view returns (Permit memory permit) { - // TODO: Implement signing logic here - permit = Permit({ amount: 10 ether, deadline: block.timestamp + 1 hours, v: 27, r: bytes32(0), s: bytes32(0) }); - } - function test_funds_splitting_with_pufETH() public { uint256 vtAmount = 2000 ether; // Want to mint 2000 VTs address recipient = actors[0]; From 7545629809e7f4ccc3d444b944fbf40114cda019 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 15:01:32 +0100 Subject: [PATCH 23/37] update solhint, remove unused import --- mainnet-contracts/.solhint.json | 5 +++-- mainnet-contracts/src/PufferModuleManager.sol | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mainnet-contracts/.solhint.json b/mainnet-contracts/.solhint.json index 996a2a2b..086e1e33 100644 --- a/mainnet-contracts/.solhint.json +++ b/mainnet-contracts/.solhint.json @@ -21,6 +21,7 @@ "func-param-name-mixedcase": "error", "modifier-name-mixedcase": "error", "code-complexity": "error", - "explicit-types": "error" + "explicit-types": "error", + "gas-custom-errors": "off" } -} \ No newline at end of file +} diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 7dd78196..e4576c31 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -14,7 +14,6 @@ import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol"; import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { IDelegationManager } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { IDelegationManagerTypes } from "../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; From 0e0c8d0c570db086f35e2dc91e0e22b4778ca7d2 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 17 Dec 2024 15:06:33 +0100 Subject: [PATCH 24/37] delete unused mocks --- .../mocks/EigenLayerDelegationManagerMock.sol | 323 ------------------ .../test/mocks/MockRevenueDepositor.sol | 8 - mainnet-contracts/test/mocks/PausableMock.sol | 130 ------- .../test/mocks/StructuredLinkedListMock.sol | 258 -------------- .../test/mocks/stETHStrategyTestnet.sol | 21 -- 5 files changed, 740 deletions(-) delete mode 100644 mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol delete mode 100644 mainnet-contracts/test/mocks/MockRevenueDepositor.sol delete mode 100644 mainnet-contracts/test/mocks/PausableMock.sol delete mode 100644 mainnet-contracts/test/mocks/StructuredLinkedListMock.sol delete mode 100644 mainnet-contracts/test/mocks/stETHStrategyTestnet.sol diff --git a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol b/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol deleted file mode 100644 index a498c525..00000000 --- a/mainnet-contracts/test/mocks/EigenLayerDelegationManagerMock.sol +++ /dev/null @@ -1,323 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; - -contract EigenLayerDelegationManagerMock is IDelegationManager { - /** - * @dev Initializes the initial owner and paused status. - */ - function initialize(address initialOwner, uint256 initialPausedStatus) external { } - - /** - * @notice Registers the caller as an operator in EigenLayer. - * @param initDelegationApprover is an address that, if set, must provide a signature when stakers delegate - * to an operator. - * @param allocationDelay The delay before allocations take effect. - * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator. - * - * @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself". - * @dev This function will revert if the caller is already delegated to an operator. - * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event - */ - function registerAsOperator(address initDelegationApprover, uint32 allocationDelay, string calldata metadataURI) - external - { } - - /** - * @notice Updates an operator's stored `delegationApprover`. - * @param operator is the operator to update the delegationApprover for - * @param newDelegationApprover is the new delegationApprover for the operator - * - * @dev The caller must have previously registered as an operator in EigenLayer. - */ - function modifyOperatorDetails(address operator, address newDelegationApprover) external { } - - /** - * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated. - * @param operator The operator to update metadata for - * @param metadataURI The URI for metadata associated with an operator - * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event - */ - function updateOperatorMetadataURI(address operator, string calldata metadataURI) external { } - - /** - * @notice Caller delegates their stake to an operator. - * @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer. - * @param approverSignatureAndExpiry Verifies the operator approves of this delegation - * @param approverSalt A unique single use value tied to an individual signature. - * @dev The approverSignatureAndExpiry is used in the event that the operator's `delegationApprover` address is set to a non-zero value. - * @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input - * in this case to save on complexity + gas costs - * @dev If the staker delegating has shares in a strategy that the operator was slashed 100% for (the operator's maxMagnitude = 0), - * then delegation is blocked and will revert. - */ - function delegateTo(address operator, SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt) - external - { } - - /** - * @notice Undelegates the staker from the operator who they are delegated to. - * Queues withdrawals of all of the staker's withdrawable shares in the StrategyManager (to the staker) and/or EigenPodManager, if necessary. - * @param staker The account to be undelegated. - * @return withdrawalRoots The roots of the newly queued withdrawals, if a withdrawal was queued. Otherwise just bytes32(0). - * - * @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves. - * @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover" - * @dev Reverts if the `staker` is already undelegated. - */ - function undelegate(address staker) external returns (bytes32[] memory withdrawalRoots) { } - - /** - * @notice Undelegates the staker from their current operator, and redelegates to `newOperator` - * Queues a withdrawal for all of the staker's withdrawable shares. These shares will only be - * delegated to `newOperator` AFTER the withdrawal is completed. - * @dev This method acts like a call to `undelegate`, then `delegateTo` - * @param newOperator the new operator that will be delegated all assets - * @dev NOTE: the following 2 params are ONLY checked if `newOperator` has a `delegationApprover`. - * If not, they can be left empty. - * @param newOperatorApproverSig A signature from the operator's `delegationApprover` - * @param approverSalt A unique single use value tied to the approver's signature - */ - function redelegate(address newOperator, SignatureWithExpiry memory newOperatorApproverSig, bytes32 approverSalt) - external - returns (bytes32[] memory withdrawalRoots) - { } - - /** - * @notice Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed - * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from - * their operator. - * - * All withdrawn shares/strategies are placed in a queue and can be withdrawn after a delay. Withdrawals - * are still subject to slashing during the delay period so the amount withdrawn on completion may actually be less - * than what was queued if slashing has occurred in that period. - * - * @dev To view what the staker is able to queue withdraw, see `getWithdrawableShares()` - */ - function queueWithdrawals(QueuedWithdrawalParams[] calldata params) external returns (bytes32[] memory) { } - - /** - * @notice Used to complete the all queued withdrawals. - * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` - * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. - * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. - * @param numToComplete The number of withdrawals to complete. This must be less than or equal to the number of queued withdrawals. - * @dev See `completeQueuedWithdrawal` for relevant dev tags - */ - function completeQueuedWithdrawals( - IERC20[][] calldata tokens, - bool[] calldata receiveAsTokens, - uint256 numToComplete - ) external { } - - /** - * @notice Used to complete the latest queued withdrawal. - * @param withdrawal The withdrawal to complete. - * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array. - * @param receiveAsTokens If true, the shares calculated to be withdrawn will be withdrawn from the specified strategies themselves - * and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies - * will simply be transferred to the caller directly. - * @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that - * any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in - * any other strategies, which will be transferred to the withdrawer. - */ - function completeQueuedWithdrawal(Withdrawal calldata withdrawal, IERC20[] calldata tokens, bool receiveAsTokens) - external - { } - - /** - * @notice Used to complete the all queued withdrawals. - * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` - * @param withdrawals Array of Withdrawals to complete. See `completeQueuedWithdrawal` for the usage of a single Withdrawal. - * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. - * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. - * @dev See `completeQueuedWithdrawal` for relevant dev tags - */ - function completeQueuedWithdrawals( - Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - bool[] calldata receiveAsTokens - ) external { } - - /** - * @notice Increases a staker's delegated share balance in a strategy. Note that before adding to operator shares, - * the delegated delegatedShares. The staker's depositScalingFactor is updated here. - * @param staker The address to increase the delegated shares for their operator. - * @param strategy The strategy in which to increase the delegated shares. - * @param prevDepositShares The number of deposit shares the staker already had in the strategy. This is the shares amount stored in the - * StrategyManager/EigenPodManager for the staker's shares. - * @param addedShares The number of shares added to the staker's shares in the strategy - * - * @dev *If the staker is actively delegated*, then increases the `staker`'s delegated delegatedShares in `strategy`. - * Otherwise does nothing. - * @dev If the operator was slashed 100% for the strategy (the operator's maxMagnitude = 0), then increasing delegated shares is blocked and will revert. - * @dev Callable only by the StrategyManager or EigenPodManager. - */ - function increaseDelegatedShares(address staker, IStrategy strategy, uint256 prevDepositShares, uint256 addedShares) - external - { } - - /** - * @notice If the staker is delegated, decreases its operator's shares in response to - * a decrease in balance in the beaconChainETHStrategy - * @param staker the staker whose operator's balance will be decreased - * @param curDepositShares the current deposit shares held by the staker - * @param beaconChainSlashingFactorDecrease the amount that the staker's beaconChainSlashingFactor has decreased by - * @dev Note: `beaconChainSlashingFactorDecrease` are assumed to ALWAYS be < 1 WAD. - * These invariants are maintained in the EigenPodManager. - */ - function decreaseDelegatedShares(address staker, uint256 curDepositShares, uint64 beaconChainSlashingFactorDecrease) - external - { } - - /** - * @notice Decreases the operators shares in storage after a slash and burns the corresponding Strategy shares - * by calling into the StrategyManager or EigenPodManager to burn the shares. - * @param operator The operator to decrease shares for - * @param strategy The strategy to decrease shares for - * @param prevMaxMagnitude the previous maxMagnitude of the operator - * @param newMaxMagnitude the new maxMagnitude of the operator - * @dev Callable only by the AllocationManager - * @dev Note: Assumes `prevMaxMagnitude <= newMaxMagnitude`. This invariant is maintained in - * the AllocationManager. - */ - function burnOperatorShares(address operator, IStrategy strategy, uint64 prevMaxMagnitude, uint64 newMaxMagnitude) - external - { } - - /** - * - * VIEW FUNCTIONS - * - */ - - /** - * @notice returns the address of the operator that `staker` is delegated to. - * @notice Mapping: staker => operator whom the staker is currently delegated to. - * @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator. - */ - function delegatedTo(address staker) external view returns (address) { } - - /** - * @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover. - * @dev Salts are used in the `delegateTo` function. Note that this function only processes the delegationApprover's - * signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`. - */ - function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool) { } - - /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated. - /// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes. - function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) { } - - /** - * @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise. - */ - function isDelegated(address staker) external view returns (bool) { } - - /** - * @notice Returns true is an operator has previously registered for delegation. - */ - function isOperator(address operator) external view returns (bool) { } - - /** - * @notice Returns the delegationApprover account for an operator - */ - function delegationApprover(address operator) external view returns (address) { } - - /** - * @notice Returns the shares that an operator has delegated to them in a set of strategies - * @param operator the operator to get shares for - * @param strategies the strategies to get shares for - */ - function getOperatorShares(address operator, IStrategy[] memory strategies) - external - view - returns (uint256[] memory) - { } - - /** - * @notice Returns the shares that a set of operators have delegated to them in a set of strategies - * @param operators the operators to get shares for - * @param strategies the strategies to get shares for - */ - function getOperatorsShares(address[] memory operators, IStrategy[] memory strategies) - external - view - returns (uint256[][] memory) - { } - - /** - * @notice Returns amount of withdrawable shares from an operator for a strategy that is still in the queue - * and therefore slashable. Note that the *actual* slashable amount could be less than this value as this doesn't account - * for amounts that have already been slashed. This assumes that none of the shares have been slashed. - * @param operator the operator to get shares for - * @param strategy the strategy to get shares for - * @return the amount of shares that are slashable in the withdrawal queue for an operator and a strategy - */ - function getSlashableSharesInQueue(address operator, IStrategy strategy) external view returns (uint256) { } - - /** - * @notice Given a staker and a set of strategies, return the shares they can queue for withdrawal and the - * corresponding depositShares. - * This value depends on which operator the staker is delegated to. - * The shares amount returned is the actual amount of Strategy shares the staker would receive (subject - * to each strategy's underlying shares to token ratio). - */ - function getWithdrawableShares(address staker, IStrategy[] memory strategies) - external - view - returns (uint256[] memory withdrawableShares, uint256[] memory depositShares) - { } - - /** - * @notice Returns the number of shares in storage for a staker and all their strategies - */ - function getDepositedShares(address staker) external view returns (IStrategy[] memory, uint256[] memory) { } - - /** - * @notice Returns the scaling factor applied to a staker's deposits for a given strategy - */ - function depositScalingFactor(address staker, IStrategy strategy) external view returns (uint256) { } - - /** - * @notice Returns the minimum withdrawal delay in blocks to pass for withdrawals queued to be completable. - * Also applies to legacy withdrawals so any withdrawals not completed prior to the slashing upgrade will be subject - * to this longer delay. - */ - function MIN_WITHDRAWAL_DELAY_BLOCKS() external view returns (uint32) { } - - /// @notice Returns a list of pending queued withdrawals for a `staker`, and the `shares` to be withdrawn. - function getQueuedWithdrawals(address staker) - external - view - returns (Withdrawal[] memory withdrawals, uint256[][] memory shares) - { } - - /// @notice Returns the keccak256 hash of `withdrawal`. - function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) { } - - /** - * @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` function. - * @param staker The account delegating their stake - * @param operator The account receiving delegated stake - * @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general) - * @param approverSalt A unique and single use value associated with the approver signature. - * @param expiry Time after which the approver's signature becomes invalid - */ - function calculateDelegationApprovalDigestHash( - address staker, - address operator, - address _delegationApprover, - bytes32 approverSalt, - uint256 expiry - ) external view returns (bytes32) { } - - /// @notice return address of the beaconChainETHStrategy - function beaconChainETHStrategy() external view returns (IStrategy) { } - - /// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract - function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) { } -} diff --git a/mainnet-contracts/test/mocks/MockRevenueDepositor.sol b/mainnet-contracts/test/mocks/MockRevenueDepositor.sol deleted file mode 100644 index a866e74f..00000000 --- a/mainnet-contracts/test/mocks/MockRevenueDepositor.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -contract MockRevenueDepositor { - function getPendingDistributionAmount() external pure returns (uint256) { - return 0; - } -} diff --git a/mainnet-contracts/test/mocks/PausableMock.sol b/mainnet-contracts/test/mocks/PausableMock.sol deleted file mode 100644 index d8f5b17e..00000000 --- a/mainnet-contracts/test/mocks/PausableMock.sol +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity >=0.8.12; - -import "src/interface/Eigenlayer-Slashing/IPausable.sol"; - -/** - * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions. - * These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control. - * @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality. - * Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code. - * For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause, - * you can only flip (any number of) switches to off/0 (aka "paused"). - * If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will: - * 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256) - * 2) update the paused state to this new value - * @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3` - * indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused - */ -contract PausableMock is IPausable { - /// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing). - IPauserRegistry public pauserRegistry; - - /// @dev whether or not the contract is currently paused - uint256 private _paused; - - uint256 internal constant UNPAUSE_ALL = 0; - uint256 internal constant PAUSE_ALL = type(uint256).max; - - /// @notice - modifier onlyPauser() { - require(pauserRegistry.isPauser(msg.sender), "msg.sender is not permissioned as pauser"); - _; - } - - modifier onlyUnpauser() { - require(msg.sender == pauserRegistry.unpauser(), "msg.sender is not permissioned as unpauser"); - _; - } - - /// @notice Throws if the contract is paused, i.e. if any of the bits in `_paused` is flipped to 1. - modifier whenNotPaused() { - require(_paused == 0, "Pausable: contract is paused"); - _; - } - - /// @notice Throws if the `indexed`th bit of `_paused` is 1, i.e. if the `index`th pause switch is flipped. - modifier onlyWhenNotPaused(uint8 index) { - require(!paused(index), "Pausable: index is paused"); - _; - } - - /// @notice One-time function for setting the `pauserRegistry` and initializing the value of `_paused`. - function _initializePauser(IPauserRegistry _pauserRegistry, uint256 initPausedStatus) internal { - require( - address(pauserRegistry) == address(0) && address(_pauserRegistry) != address(0), - "Pausable._initializePauser: _initializePauser() can only be called once" - ); - _paused = initPausedStatus; - emit Paused(msg.sender, initPausedStatus); - _setPauserRegistry(_pauserRegistry); - } - - /** - * @notice This function is used to pause an EigenLayer contract's functionality. - * It is permissioned to the `pauser` address, which is expected to be a low threshold multisig. - * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once. - * @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0. - */ - function pause(uint256 newPausedStatus) external onlyPauser { - // verify that the `newPausedStatus` does not *unflip* any bits (i.e. doesn't unpause anything, all 1 bits remain) - require((_paused & newPausedStatus) == _paused, "Pausable.pause: invalid attempt to unpause functionality"); - _paused = newPausedStatus; - emit Paused(msg.sender, newPausedStatus); - } - - /** - * @notice Alias for `pause(type(uint256).max)`. - */ - function pauseAll() external onlyPauser { - _paused = type(uint256).max; - emit Paused(msg.sender, type(uint256).max); - } - - /** - * @notice This function is used to unpause an EigenLayer contract's functionality. - * It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract. - * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once. - * @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1. - */ - function unpause(uint256 newPausedStatus) external onlyUnpauser { - // verify that the `newPausedStatus` does not *flip* any bits (i.e. doesn't pause anything, all 0 bits remain) - require( - ((~_paused) & (~newPausedStatus)) == (~_paused), "Pausable.unpause: invalid attempt to pause functionality" - ); - _paused = newPausedStatus; - emit Unpaused(msg.sender, newPausedStatus); - } - - /// @notice Returns the current paused status as a uint256. - function paused() public view virtual returns (uint256) { - return _paused; - } - - /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise - function paused(uint8 index) public view virtual returns (bool) { - uint256 mask = 1 << index; - return ((_paused & mask) == mask); - } - - /// internal function for setting pauser registry - function _setPauserRegistry(IPauserRegistry newPauserRegistry) internal { - require( - address(newPauserRegistry) != address(0), - "Pausable._setPauserRegistry: newPauserRegistry cannot be the zero address" - ); - // emit PauserRegistrySet(pauserRegistry, newPauserRegistry); - pauserRegistry = newPauserRegistry; - } - - /** - * @dev This empty reserved space is put in place to allow future versions to add new - * variables without shifting down storage in the inheritance chain. - * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - */ - uint256[48] private __gap; -} diff --git a/mainnet-contracts/test/mocks/StructuredLinkedListMock.sol b/mainnet-contracts/test/mocks/StructuredLinkedListMock.sol deleted file mode 100644 index 7e669660..00000000 --- a/mainnet-contracts/test/mocks/StructuredLinkedListMock.sol +++ /dev/null @@ -1,258 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.12; - -/** - * @title StructuredLinkedList - * @author Vittorio Minacori (https://github.com/vittominacori) - * @dev An utility library for using sorted linked list data structures in your Solidity project. - * @notice Adapted from https://github.com/vittominacori/solidity-linked-list/blob/master/contracts/StructuredLinkedList.sol - */ -library StructuredLinkedListMock { - uint256 private constant _NULL = 0; - uint256 private constant _HEAD = 0; - - bool private constant _PREV = false; - bool private constant _NEXT = true; - - struct List { - uint256 size; - mapping(uint256 => mapping(bool => uint256)) list; - } - - /** - * @dev Checks if the list exists - * @param self stored linked list from contract - * @return bool true if list exists, false otherwise - */ - function listExists(List storage self) internal view returns (bool) { - // if the head nodes previous or next pointers both point to itself, then there are no items in the list - if (self.list[_HEAD][_PREV] != _HEAD || self.list[_HEAD][_NEXT] != _HEAD) { - return true; - } else { - return false; - } - } - - /** - * @dev Checks if the node exists - * @param self stored linked list from contract - * @param _node a node to search for - * @return bool true if node exists, false otherwise - */ - function nodeExists(List storage self, uint256 _node) internal view returns (bool) { - if (self.list[_node][_PREV] == _HEAD && self.list[_node][_NEXT] == _HEAD) { - if (self.list[_HEAD][_NEXT] == _node) { - return true; - } else { - return false; - } - } else { - return true; - } - } - - /** - * @dev Returns the number of elements in the list - * @param self stored linked list from contract - * @return uint256 - */ - function sizeOf(List storage self) internal view returns (uint256) { - return self.size; - } - - /** - * @dev Gets the head of the list - * @param self stored linked list from contract - * @return uint256 the head of the list - */ - function getHead(List storage self) internal view returns (uint256) { - return self.list[_HEAD][_NEXT]; - } - - /** - * @dev Returns the links of a node as a tuple - * @param self stored linked list from contract - * @param _node id of the node to get - * @return bool, uint256, uint256 true if node exists or false otherwise, previous node, next node - */ - function getNode(List storage self, uint256 _node) internal view returns (bool, uint256, uint256) { - if (!nodeExists(self, _node)) { - return (false, 0, 0); - } else { - return (true, self.list[_node][_PREV], self.list[_node][_NEXT]); - } - } - - /** - * @dev Returns the link of a node `_node` in direction `_direction`. - * @param self stored linked list from contract - * @param _node id of the node to step from - * @param _direction direction to step in - * @return bool, uint256 true if node exists or false otherwise, node in _direction - */ - function getAdjacent(List storage self, uint256 _node, bool _direction) internal view returns (bool, uint256) { - if (!nodeExists(self, _node)) { - return (false, 0); - } else { - uint256 adjacent = self.list[_node][_direction]; - return (adjacent != _HEAD, adjacent); - } - } - - /** - * @dev Returns the link of a node `_node` in direction `_NEXT`. - * @param self stored linked list from contract - * @param _node id of the node to step from - * @return bool, uint256 true if node exists or false otherwise, next node - */ - function getNextNode(List storage self, uint256 _node) internal view returns (bool, uint256) { - return getAdjacent(self, _node, _NEXT); - } - - /** - * @dev Returns the link of a node `_node` in direction `_PREV`. - * @param self stored linked list from contract - * @param _node id of the node to step from - * @return bool, uint256 true if node exists or false otherwise, previous node - */ - function getPreviousNode(List storage self, uint256 _node) internal view returns (bool, uint256) { - return getAdjacent(self, _node, _PREV); - } - - /** - * @dev Insert node `_new` beside existing node `_node` in direction `_NEXT`. - * @param self stored linked list from contract - * @param _node existing node - * @param _new new node to insert - * @return bool true if success, false otherwise - */ - function insertAfter(List storage self, uint256 _node, uint256 _new) internal returns (bool) { - return _insert(self, _node, _new, _NEXT); - } - - /** - * @dev Insert node `_new` beside existing node `_node` in direction `_PREV`. - * @param self stored linked list from contract - * @param _node existing node - * @param _new new node to insert - * @return bool true if success, false otherwise - */ - function insertBefore(List storage self, uint256 _node, uint256 _new) internal returns (bool) { - return _insert(self, _node, _new, _PREV); - } - - /** - * @dev Removes an entry from the linked list - * @param self stored linked list from contract - * @param _node node to remove from the list - * @return uint256 the removed node - */ - function remove(List storage self, uint256 _node) internal returns (uint256) { - if ((_node == _NULL) || (!nodeExists(self, _node))) { - return 0; - } - _createLink(self, self.list[_node][_PREV], self.list[_node][_NEXT], _NEXT); - delete self.list[_node][_PREV]; - delete self.list[_node][_NEXT]; - - self.size -= 1; // NOT: SafeMath library should be used here to decrement. - - return _node; - } - - /** - * @dev Pushes an entry to the head of the linked list - * @param self stored linked list from contract - * @param _node new entry to push to the head - * @return bool true if success, false otherwise - */ - function pushFront(List storage self, uint256 _node) internal returns (bool) { - return _push(self, _node, _NEXT); - } - - /** - * @dev Pushes an entry to the tail of the linked list - * @param self stored linked list from contract - * @param _node new entry to push to the tail - * @return bool true if success, false otherwise - */ - function pushBack(List storage self, uint256 _node) internal returns (bool) { - return _push(self, _node, _PREV); - } - - /** - * @dev Pops the first entry from the head of the linked list - * @param self stored linked list from contract - * @return uint256 the removed node - */ - function popFront(List storage self) internal returns (uint256) { - return _pop(self, _NEXT); - } - - /** - * @dev Pops the first entry from the tail of the linked list - * @param self stored linked list from contract - * @return uint256 the removed node - */ - function popBack(List storage self) internal returns (uint256) { - return _pop(self, _PREV); - } - - /** - * @dev Pushes an entry to the head of the linked list - * @param self stored linked list from contract - * @param _node new entry to push to the head - * @param _direction push to the head (_NEXT) or tail (_PREV) - * @return bool true if success, false otherwise - */ - function _push(List storage self, uint256 _node, bool _direction) private returns (bool) { - return _insert(self, _HEAD, _node, _direction); - } - - /** - * @dev Pops the first entry from the linked list - * @param self stored linked list from contract - * @param _direction pop from the head (_NEXT) or the tail (_PREV) - * @return uint256 the removed node - */ - function _pop(List storage self, bool _direction) private returns (uint256) { - uint256 adj; - (, adj) = getAdjacent(self, _HEAD, _direction); - return remove(self, adj); - } - - /** - * @dev Insert node `_new` beside existing node `_node` in direction `_direction`. - * @param self stored linked list from contract - * @param _node existing node - * @param _new new node to insert - * @param _direction direction to insert node in - * @return bool true if success, false otherwise - */ - function _insert(List storage self, uint256 _node, uint256 _new, bool _direction) private returns (bool) { - if (!nodeExists(self, _new) && nodeExists(self, _node)) { - uint256 c = self.list[_node][_direction]; - _createLink(self, _node, _new, _direction); - _createLink(self, _new, c, _direction); - - self.size += 1; // NOT: SafeMath library should be used here to increment. - - return true; - } - - return false; - } - - /** - * @dev Creates a bidirectional link between two nodes on direction `_direction` - * @param self stored linked list from contract - * @param _node existing node - * @param _link node to link to in the _direction - * @param _direction direction to insert node in - */ - function _createLink(List storage self, uint256 _node, uint256 _link, bool _direction) private { - self.list[_link][!_direction] = _node; - self.list[_node][_direction] = _link; - } -} diff --git a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol b/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol deleted file mode 100644 index a86f4497..00000000 --- a/mainnet-contracts/test/mocks/stETHStrategyTestnet.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; - -contract stETHStrategyTestnet is IStrategy { - /** - * @notice Returns the amount of underlying tokens for `user` - */ - function userUnderlying(address) external pure returns (uint256) { - return 0; - } - - function userUnderlyingView(address) external pure returns (uint256) { - return 0; - } - - function sharesToUnderlyingView(uint256) external pure returns (uint256) { - return 0; - } -} From 4d101097dc3c02851c86e0721db600db7a6602b7 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 18 Dec 2024 10:04:30 +0100 Subject: [PATCH 25/37] increase test coverage --- mainnet-contracts/package.json | 4 +- .../07_GenerateSlashingELCalldata.s.sol | 27 +++++ .../script/CompleteQueuedWithdrawals.s.sol | 1 - mainnet-contracts/script/DeployPuffer.s.sol | 5 +- mainnet-contracts/src/PufferModuleManager.sol | 10 ++ mainnet-contracts/src/RestakingOperator.sol | 3 + .../Eigenlayer-Slashing/IAVSDirectory.sol | 82 ------------- .../src/interface/IPufferModuleManager.sol | 23 ++++ .../PufferModuleManager.integration.t.sol | 24 +--- ...anagerHoleskyTestnetTest.integration.t.sol | 23 ---- .../ValidatorTicketMainnetTest.fork.t.sol | 2 +- .../ffi/PufferModuleManagerHoleskyFfi.t.sol | 26 ---- .../test/helpers/UnitTestHelper.sol | 1 + .../test/mocks/DelegationManagerMock.sol | 12 +- .../test/mocks/EigenAllocationManagerMock.sol | 14 +++ .../test/unit/PufferModuleManager.t.sol | 114 ++++++++++++++++++ 16 files changed, 202 insertions(+), 169 deletions(-) create mode 100644 mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol create mode 100644 mainnet-contracts/test/mocks/EigenAllocationManagerMock.sol diff --git a/mainnet-contracts/package.json b/mainnet-contracts/package.json index e5c4ca12..952de4d2 100644 --- a/mainnet-contracts/package.json +++ b/mainnet-contracts/package.json @@ -47,7 +47,7 @@ "lint": "yarn run lint:sol", "test:unit": "forge test --mp \"./test/unit/**/*.sol\" -vvv", "slither": "slither .", - "coverage": "forge coverage --force --no-match-coverage \"(script|test|mock|node_modules|integrations|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\"", - "coverage-lcov": "forge coverage --force --no-match-coverage \"(script|test|mock|node_modules|integrations|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\" --report lcov" + "coverage": "forge coverage --force --no-match-coverage \"(script|test|mock|interface|node_modules|integrations|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\"", + "coverage-lcov": "forge coverage --force --no-match-coverage \"(script|test|mock|interface|node_modules|integrations|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\" --report lcov" } } diff --git a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol new file mode 100644 index 00000000..358299a5 --- /dev/null +++ b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import { Script } from "forge-std/Script.sol"; +import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; +import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol"; +import { ROLE_ID_DAO } from "../../script/Roles.sol"; +import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; + +contract GenerateSlashingELCalldata is Script { + function run(address pufferModuleManagerProxy) public pure returns (bytes memory) { + bytes[] memory calldatas = new bytes[](1); + + bytes4[] memory daoSelectors = new bytes4[](4); + daoSelectors[0] = PufferModuleManager.callModifyOperatorDetails.selector; + daoSelectors[1] = PufferModuleManager.callUpdateMetadataURI.selector; + daoSelectors[2] = PufferModuleManager.callRegisterOperatorToAVS.selector; + daoSelectors[3] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; + + calldatas[0] = + abi.encodeCall(AccessManager.setTargetFunctionRole, (pufferModuleManagerProxy, daoSelectors, ROLE_ID_DAO)); + + bytes memory encodedMulticall = abi.encodeCall(Multicall.multicall, (calldatas)); + + return encodedMulticall; + } +} diff --git a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol index 7db45c08..18627741 100644 --- a/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol +++ b/mainnet-contracts/script/CompleteQueuedWithdrawals.s.sol @@ -63,7 +63,6 @@ contract CompleteQueuedWithdrawals is Script { IERC20[][] memory tokens = new IERC20[][](1); tokens[0] = t; - uint256[] memory middlewareTimesIndexes = new uint256[](1); // 0 vm.startBroadcast(); PufferModuleManager(payable(params.pufferModuleManager)).callCompleteQueuedWithdrawals({ diff --git a/mainnet-contracts/script/DeployPuffer.s.sol b/mainnet-contracts/script/DeployPuffer.s.sol index 8b041181..4606b4a5 100644 --- a/mainnet-contracts/script/DeployPuffer.s.sol +++ b/mainnet-contracts/script/DeployPuffer.s.sol @@ -27,7 +27,7 @@ import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol"; import { IRewardsCoordinator } from "../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { AVSContractsRegistry } from "../src/AVSContractsRegistry.sol"; import { RewardsCoordinatorMock } from "../test/mocks/RewardsCoordinatorMock.sol"; - +import { EigenAllocationManagerMock } from "../test/mocks/EigenAllocationManagerMock.sol"; /** * @title DeployPuffer * @author Puffer Finance @@ -43,6 +43,7 @@ import { RewardsCoordinatorMock } from "../test/mocks/RewardsCoordinatorMock.sol * * forge script script/DeployPuffer.s.sol:DeployPuffer -vvvv --rpc-url=$EPHEMERY_RPC_URL --broadcast */ + contract DeployPuffer is BaseScript { PufferProtocol pufferProtocolImpl; AccessManager accessManager; @@ -84,7 +85,7 @@ contract DeployPuffer is BaseScript { eigenPodManager = address(new EigenPodManagerMock()); delegationManager = address(new DelegationManagerMock()); rewardsCoordinator = address(new RewardsCoordinatorMock()); - eigenSlasher = vm.envOr("EIGEN_SLASHER", address(1)); // @todo + eigenSlasher = address(new EigenAllocationManagerMock()); treasury = address(1); operationsMultisig = address(2); } else { diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index e4576c31..6f243dc7 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -243,6 +243,12 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, IAllocationManager.RegisterParams calldata registrationParams ) external virtual restricted { restakingOperator.registerOperatorToAVS(registrationParams); + emit RestakingOperatorRegisteredToAVS( + address(restakingOperator), + registrationParams.avs, + registrationParams.operatorSetIds, + registrationParams.data + ); } /** @@ -276,6 +282,10 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, IAllocationManager.DeregisterParams calldata deregistrationParams ) external virtual restricted { restakingOperator.deregisterOperatorFromAVS(deregistrationParams); + + emit RestakingOperatorDeregisteredFromAVS( + address(restakingOperator), deregistrationParams.avs, deregistrationParams.operatorSetIds + ); } /** diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index e1211616..306ebec4 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -87,6 +87,9 @@ contract RestakingOperator is IERC1271, Initializable, AccessManagedUpgradeable if (address(moduleManager) == address(0)) { revert InvalidAddress(); } + if (address(rewardsCoordinator) == address(0)) { + revert InvalidAddress(); + } EIGEN_DELEGATION_MANAGER = delegationManager; EIGEN_ALLOCATION_MANAGER = allocationManager; PUFFER_MODULE_MANAGER = moduleManager; diff --git a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol index b145d60c..91434f9d 100644 --- a/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol +++ b/mainnet-contracts/src/interface/Eigenlayer-Slashing/IAVSDirectory.sol @@ -58,85 +58,3 @@ interface IAVSDirectoryEvents is IAVSDirectoryTypes { /// @notice Emitted when an operator is migrated from M2 registration to operator sets. event OperatorMigratedToOperatorSets(address indexed operator, address indexed avs, uint32[] operatorSetIds); } - -interface IAVSDirectory is IAVSDirectoryEvents, IAVSDirectoryErrors, ISignatureUtils { - /** - * - * EXTERNAL FUNCTIONS - * - */ - - /** - * @dev Initializes the addresses of the initial owner and paused status. - */ - function initialize(address initialOwner, uint256 initialPausedStatus) external; - - /** - * @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated. - * - * @param metadataURI The URI for metadata associated with an AVS. - * - * @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event. - */ - function updateAVSMetadataURI(string calldata metadataURI) external; - - /** - * @notice Called by an operator to cancel a salt that has been used to register with an AVS. - * - * @param salt A unique and single use value associated with the approver signature. - */ - function cancelSalt(bytes32 salt) external; - - /** - * @notice Legacy function called by the AVS's service manager contract - * to register an operator with the AVS. NOTE: this function will be deprecated in a future release - * after the slashing release. New AVSs should use `registerForOperatorSets` instead. - * - * @param operator The address of the operator to register. - * @param operatorSignature The signature, salt, and expiry of the operator's signature. - * - * @dev msg.sender must be the AVS. - * @dev Only used by legacy M2 AVSs that have not integrated with operator sets. - */ - function registerOperatorToAVS( - address operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature - ) external; - - /** - * @notice Legacy function called by an AVS to deregister an operator from the AVS. - * NOTE: this function will be deprecated in a future release after the slashing release. - * New AVSs integrating should use `deregisterOperatorFromOperatorSets` instead. - * - * @param operator The address of the operator to deregister. - * - * @dev Only used by legacy M2 AVSs that have not integrated with operator sets. - */ - function deregisterOperatorFromAVS(address operator) external; - - /** - * - * VIEW FUNCTIONS - * - */ - function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool); - - /** - * @notice Calculates the digest hash to be signed by an operator to register with an AVS. - * - * @param operator The account registering as an operator. - * @param avs The AVS the operator is registering with. - * @param salt A unique and single-use value associated with the approver's signature. - * @param expiry The time after which the approver's signature becomes invalid. - */ - function calculateOperatorAVSRegistrationDigestHash(address operator, address avs, bytes32 salt, uint256 expiry) - external - view - returns (bytes32); - - /// @notice The EIP-712 typehash for the Registration struct used by the contract. - function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32); - - /// @notice The EIP-712 typehash for the OperatorSetRegistration struct used by the contract. - function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32); -} diff --git a/mainnet-contracts/src/interface/IPufferModuleManager.sol b/mainnet-contracts/src/interface/IPufferModuleManager.sol index 6e077e84..e9d72116 100644 --- a/mainnet-contracts/src/interface/IPufferModuleManager.sol +++ b/mainnet-contracts/src/interface/IPufferModuleManager.sol @@ -20,6 +20,29 @@ interface IPufferModuleManager { */ event CustomCallSucceeded(address indexed restakingOperator, address target, bytes customCalldata, bytes response); + /** + * @notice Emitted when the Restaking Operator is registered to an AVS + * @param restakingOperator is the address of the restaking operator + * @param avs is the address of the AVS + * @param operatorSetId is the id of the operator set + * @param data is the data passed to the AVS + * @dev Signature "0xe47a1be2e87cd0d8e7deac93187c98c837de2096e1f048141ab6e377d30d648a" + */ + event RestakingOperatorRegisteredToAVS( + address indexed restakingOperator, address indexed avs, uint32[] operatorSetId, bytes data + ); + + /** + * @notice Emitted when the Restaking Operator is deregistered from an AVS + * @param restakingOperator is the address of the restaking operator + * @param avs is the address of the AVS + * @param operatorSetId is the id of the operator set + * @dev Signature "0xd3a1da1a6a02235e5cee67b27f99931e657829be79f720ae8bfe10bd80bcd5ae" + */ + event RestakingOperatorDeregisteredFromAVS( + address indexed restakingOperator, address indexed avs, uint32[] operatorSetId + ); + /** * @notice Emitted when a Restaking Operator is opted into a slasher * @param restakingOperator is the address of the restaking operator diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index 60b14086..ddd33fca 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -11,7 +11,6 @@ import { DeployEverything } from "script/DeployEverything.s.sol"; import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IStrategyManager } from "../../src/interface/Eigenlayer-Slashing/IStrategyManager.sol"; import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -30,7 +29,7 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { address EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY = 0x53012C69A189cfA2D9d29eb6F19B32e0A2EA3490; address EIGEN_DA_SERVICE_MANAGER = 0xD4A7E1Bd8015057293f0D0A557088c286942e84b; - IAVSDirectory public avsDirectory = IAVSDirectory(0x055733000064333CaDDbC92763c58BF0192fFeBf); + // IAVSDirectory public avsDirectory = IAVSDirectory(0x055733000064333CaDDbC92763c58BF0192fFeBf); function setUp() public { deployContractsHolesky(0); // on latest block @@ -103,27 +102,6 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { return operator; } - /** - * @notice internal function for calculating a signature from the operator corresponding to `_operatorPrivateKey`, delegating them to - * the `operator`, and expiring at `expiry`. - */ - function _getOperatorSignature( - uint256 _operatorPrivateKey, - address operator, - address avs, - bytes32 salt, - uint256 expiry - ) internal view returns (bytes32 digestHash, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) { - operatorSignature.expiry = expiry; - operatorSignature.salt = salt; - { - digestHash = avsDirectory.calculateOperatorAVSRegistrationDigestHash(operator, avs, salt, expiry); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_operatorPrivateKey, digestHash); - operatorSignature.signature = abi.encodePacked(r, s, v); - } - return (digestHash, operatorSignature); - } - function _mulGo(uint256 x) internal returns (BN254.G2Point memory g2Point) { string[] memory inputs = new string[](3); inputs[0] = "./test/helpers/go2mul"; // lib/eigenlayer-middleware/test/ffi/go/g2mul.go binary diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol index eae878d9..d9b51cec 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerHoleskyTestnetTest.integration.t.sol @@ -10,7 +10,6 @@ import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; -import { IAVSDirectory } from "../../src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; import { IRewardsCoordinator } from "../../src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; import { BN254 } from "../../src/interface/libraries/BN254.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; @@ -32,7 +31,6 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { uint256[] privKeys; // https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#deployments - IAVSDirectory public avsDirectory = IAVSDirectory(0x055733000064333CaDDbC92763c58BF0192fFeBf); address EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY = 0x53012C69A189cfA2D9d29eb6F19B32e0A2EA3490; address EIGEN_DA_SERVICE_MANAGER = 0xD4A7E1Bd8015057293f0D0A557088c286942e84b; address BEACON_CHAIN_STRATEGY = 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0; @@ -148,25 +146,4 @@ contract PufferModuleManagerHoleskyTestnetTest is Test { res = vm.ffi(inputs); g2Point.Y[0] = abi.decode(res, (uint256)); } - - /** - * @notice internal function for calculating a signature from the operator corresponding to `_operatorPrivateKey`, delegating them to - * the `operator`, and expiring at `expiry`. - */ - function _getOperatorSignature( - uint256 _operatorPrivateKey, - address operator, - address avs, - bytes32 salt, - uint256 expiry - ) internal view returns (bytes32 digestHash, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) { - operatorSignature.expiry = expiry; - operatorSignature.salt = salt; - { - digestHash = avsDirectory.calculateOperatorAVSRegistrationDigestHash(operator, avs, salt, expiry); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_operatorPrivateKey, digestHash); - operatorSignature.signature = abi.encodePacked(r, s, v); - } - return (digestHash, operatorSignature); - } } diff --git a/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol b/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol index 07f61145..6dacd13a 100644 --- a/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol +++ b/mainnet-contracts/test/fork-tests/ValidatorTicketMainnetTest.fork.t.sol @@ -48,7 +48,7 @@ contract ValidatorTicketMainnetTest is MainnetForkTestHelper { vm.stopPrank(); } - function test_initial_state() public { + function test_initial_state() public view { assertEq(validatorTicket.name(), "Puffer Validator Ticket"); assertEq(validatorTicket.symbol(), "VT"); assertEq(validatorTicket.getProtocolFeeRate(), INITIAL_PROTOCOL_FEE); diff --git a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol index 2615533b..a02f0fbb 100644 --- a/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol +++ b/mainnet-contracts/test/fork-tests/ffi/PufferModuleManagerHoleskyFfi.t.sol @@ -3,10 +3,6 @@ pragma solidity >=0.8.0 <0.9.0; import "forge-std/console.sol"; import { Test } from "forge-std/Test.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { ISignatureUtils } from "src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; -import { IAVSDirectory } from "src/interface/Eigenlayer-Slashing/IAVSDirectory.sol"; -import { IDelegationManager } from "src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { BN254 } from "src/interface/libraries/BN254.sol"; @@ -23,7 +19,6 @@ contract PufferModuleManagerHoleskyTestnetFFI is Test { uint256[] privKeys; // https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#deployments - IAVSDirectory public avsDirectory = IAVSDirectory(0x055733000064333CaDDbC92763c58BF0192fFeBf); address EIGEN_DA_REGISTRY_COORDINATOR_HOLESKY = 0x53012C69A189cfA2D9d29eb6F19B32e0A2EA3490; address EIGEN_DA_SERVICE_MANAGER = 0xD4A7E1Bd8015057293f0D0A557088c286942e84b; address BEACON_CHAIN_STRATEGY = 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0; @@ -64,25 +59,4 @@ contract PufferModuleManagerHoleskyTestnetFFI is Test { res = vm.ffi(inputs); g2Point.Y[0] = abi.decode(res, (uint256)); } - - /** - * @notice internal function for calculating a signature from the operator corresponding to `_operatorPrivateKey`, delegating them to - * the `operator`, and expiring at `expiry`. - */ - function _getOperatorSignature( - uint256 _operatorPrivateKey, - address operator, - address avs, - bytes32 salt, - uint256 expiry - ) internal view returns (bytes32 digestHash, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) { - operatorSignature.expiry = expiry; - operatorSignature.salt = salt; - { - digestHash = avsDirectory.calculateOperatorAVSRegistrationDigestHash(operator, avs, salt, expiry); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_operatorPrivateKey, digestHash); - operatorSignature.signature = abi.encodePacked(r, s, v); - } - return (digestHash, operatorSignature); - } } diff --git a/mainnet-contracts/test/helpers/UnitTestHelper.sol b/mainnet-contracts/test/helpers/UnitTestHelper.sol index a18338cc..6b0fbe78 100644 --- a/mainnet-contracts/test/helpers/UnitTestHelper.sol +++ b/mainnet-contracts/test/helpers/UnitTestHelper.sol @@ -37,6 +37,7 @@ import { ROLE_ID_OPERATIONS_MULTISIG, ROLE_ID_LOCKBOX } from "../../script/Roles.sol"; +import { GenerateSlashingELCalldata } from "../../script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol"; contract UnitTestHelper is Test, BaseScript { bytes32 private constant _PERMIT_TYPEHASH = diff --git a/mainnet-contracts/test/mocks/DelegationManagerMock.sol b/mainnet-contracts/test/mocks/DelegationManagerMock.sol index e186dcde..49f5b402 100644 --- a/mainnet-contracts/test/mocks/DelegationManagerMock.sol +++ b/mainnet-contracts/test/mocks/DelegationManagerMock.sol @@ -34,8 +34,6 @@ contract DelegationManagerMock { delegatedTo[msg.sender] = operator; } - function modifyOperatorDetails(IDelegationManager.OperatorDetails calldata /*newOperatorDetails*/ ) external pure { } - function delegateToBySignature( address, /*staker*/ address, /*operator*/ @@ -157,13 +155,6 @@ contract DelegationManagerMock { function calculateWithdrawalRoot(IDelegationManager.Withdrawal memory withdrawal) external pure returns (bytes32) { } - function registerOperatorToAVS( - address operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature - ) external { } - - function deregisterOperatorFromAVS(address operator) external { } - function operatorSaltIsSpent(address avs, bytes32 salt) external view returns (bool) { } function queueWithdrawals(IDelegationManager.QueuedWithdrawalParams[] calldata queuedWithdrawalParams) @@ -204,4 +195,7 @@ contract DelegationManagerMock { IERC20[][] calldata tokens, bool[] calldata receiveAsTokens ) external { } + + function modifyOperatorDetails(address operator, address newDelegationApprover) external pure { } + function updateOperatorMetadataURI(address operator, string calldata newMetadataURI) external pure { } } diff --git a/mainnet-contracts/test/mocks/EigenAllocationManagerMock.sol b/mainnet-contracts/test/mocks/EigenAllocationManagerMock.sol new file mode 100644 index 00000000..5439f3e9 --- /dev/null +++ b/mainnet-contracts/test/mocks/EigenAllocationManagerMock.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import { IAllocationManagerTypes } from "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; + +// New way of registering and de-registering operators to/from AVS +contract EigenAllocationManagerMock { + function registerForOperatorSets( + address operator, + IAllocationManagerTypes.RegisterParams calldata registrationParams + ) external { } + + function deregisterFromOperatorSets(IAllocationManagerTypes.DeregisterParams calldata params) external { } +} diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index e0b43565..4bf0d6d5 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -17,6 +17,11 @@ import { IDelegationManagerTypes } from "src/interface/Eigenlayer-Slashing/IDele import { RestakingOperator } from "src/RestakingOperator.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import { IStrategy } from "src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { GenerateSlashingELCalldata } from "../../script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol"; +import { IAllocationManagerTypes } from "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import { IAllocationManager } from "src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import { IRewardsCoordinator } from "src/interface/Eigenlayer-Slashing/IRewardsCoordinator.sol"; +import { InvalidAddress } from "../../src/Errors.sol"; contract PufferModuleUpgrade { function getMagicValue() external pure returns (uint256) { @@ -37,13 +42,53 @@ contract PufferModuleManagerTest is UnitTestHelper { vm.deal(address(this), 1000 ether); + bytes memory cd = new GenerateSlashingELCalldata().run(address(pufferModuleManager)); + vm.startPrank(timelock); accessManager.grantRole(ROLE_ID_OPERATIONS_PAYMASTER, address(this), 0); + (bool success,) = address(accessManager).call(cd); + assertTrue(success, "should succeed"); + vm.stopPrank(); _skipDefaultFuzzAddresses(); } + function test_createBadRestakingOperator() public { + // Bad Delegation Manager + vm.expectRevert(InvalidAddress.selector); + new RestakingOperator({ + delegationManager: IDelegationManager(address(0)), + allocationManager: IAllocationManager(address(0)), + moduleManager: IPufferModuleManager(address(0)), + rewardsCoordinator: IRewardsCoordinator(address(0)) + }); + // Bad Allocation Manager + vm.expectRevert(InvalidAddress.selector); + new RestakingOperator({ + delegationManager: IDelegationManager(address(5)), + allocationManager: IAllocationManager(address(0)), + moduleManager: IPufferModuleManager(address(0)), + rewardsCoordinator: IRewardsCoordinator(address(0)) + }); + // Bad Module Manager + vm.expectRevert(InvalidAddress.selector); + new RestakingOperator({ + delegationManager: IDelegationManager(address(5)), + allocationManager: IAllocationManager(address(6)), + moduleManager: IPufferModuleManager(address(0)), + rewardsCoordinator: IRewardsCoordinator(address(0)) + }); + // Bad Rewards Coordinator + vm.expectRevert(InvalidAddress.selector); + new RestakingOperator({ + delegationManager: IDelegationManager(address(5)), + allocationManager: IAllocationManager(address(6)), + moduleManager: IPufferModuleManager(address(7)), + rewardsCoordinator: IRewardsCoordinator(address(0)) + }); + } + function test_beaconUpgrade() public { address moduleBeacon = pufferModuleManager.PUFFER_MODULE_BEACON(); @@ -86,6 +131,75 @@ contract PufferModuleManagerTest is UnitTestHelper { PufferModule(payable(module)).call(address(0), 0, ""); } + function test_registerOperatorToAVS() public { + vm.startPrank(DAO); + RestakingOperator operator = _createRestakingOperator(); + + address mockAvs = makeAddr("mockAvs"); + + IAllocationManagerTypes.RegisterParams memory registerParams = + IAllocationManagerTypes.RegisterParams({ avs: mockAvs, operatorSetIds: new uint32[](1), data: "asdf" }); + + vm.expectRevert(Unauthorized.selector); + operator.registerOperatorToAVS(registerParams); + + vm.expectEmit(true, true, true, true); + emit IPufferModuleManager.RestakingOperatorRegisteredToAVS(address(operator), mockAvs, new uint32[](1), "asdf"); + pufferModuleManager.callRegisterOperatorToAVS(operator, registerParams); + } + + function test_deregisterOperatorFromAVS() public { + vm.startPrank(DAO); + RestakingOperator operator = _createRestakingOperator(); + + address mockAvs = makeAddr("mockAvs"); + + IAllocationManagerTypes.DeregisterParams memory deregisterParams = IAllocationManagerTypes.DeregisterParams({ + operator: address(operator), + avs: mockAvs, + operatorSetIds: new uint32[](1) + }); + + vm.expectRevert(Unauthorized.selector); + operator.deregisterOperatorFromAVS(deregisterParams); + + vm.expectEmit(true, true, true, true); + emit IPufferModuleManager.RestakingOperatorDeregisteredFromAVS(address(operator), mockAvs, new uint32[](1)); + pufferModuleManager.callDeregisterOperatorFromAVS(operator, deregisterParams); + } + + function test_modifyOperatorDetails(address newDelegationApprover) public { + vm.assume(newDelegationApprover != address(0)); + + vm.startPrank(DAO); + RestakingOperator operator = _createRestakingOperator(); + + // Can't be called directly + vm.expectRevert(Unauthorized.selector); + operator.modifyOperatorDetails(newDelegationApprover); + + // Can be called through the PufferModuleManager + vm.expectEmit(true, true, true, true); + emit IPufferModuleManager.RestakingOperatorModified(address(operator), newDelegationApprover); + pufferModuleManager.callModifyOperatorDetails(operator, newDelegationApprover); + vm.stopPrank(); + } + + function test_updateOperatorMetadataURI(string memory newMetadataURI) public { + vm.startPrank(DAO); + RestakingOperator operator = _createRestakingOperator(); + + // Can't be called directly + vm.expectRevert(Unauthorized.selector); + operator.updateOperatorMetadataURI(newMetadataURI); + + vm.startPrank(DAO); + vm.expectEmit(true, true, true, true); + emit IPufferModuleManager.RestakingOperatorMetadataURIUpdated(address(operator), newMetadataURI); + pufferModuleManager.callUpdateMetadataURI(operator, newMetadataURI); + vm.stopPrank(); + } + function test_donation(bytes32 moduleName) public { address module = _createPufferModule(moduleName); (bool s,) = address(module).call{ value: 5 ether }(""); From 2ed174dc82462f2f6d69404d074c0144cbb51eb5 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 18 Dec 2024 13:38:52 +0100 Subject: [PATCH 26/37] remove IPufferModule --- mainnet-contracts/.solhint.json | 1 - mainnet-contracts/package.json | 5 +- mainnet-contracts/remappings.txt | 1 - .../GenerateAccessManagerCalldata2.s.sol | 5 +- mainnet-contracts/src/L1RewardManager.sol | 7 - .../src/L1RewardManagerUnsafe.sol | 3 - mainnet-contracts/src/PufferModule.sol | 78 +++-------- mainnet-contracts/src/PufferModuleManager.sol | 50 +++---- mainnet-contracts/src/PufferProtocol.sol | 11 +- .../src/PufferRevenueDepositor.sol | 16 +-- mainnet-contracts/src/PufferVault.sol | 12 -- mainnet-contracts/src/PufferVaultV2.sol | 38 ------ .../src/interface/IPufferModule.sol | 124 ------------------ .../src/struct/ProtocolStorage.sol | 6 +- .../test/handlers/PufferProtocolHandler.sol | 4 +- .../test/unit/PufferModuleManager.t.sol | 14 -- .../test/unit/PufferProtocol.t.sol | 3 +- yarn.lock | 34 ++++- 18 files changed, 88 insertions(+), 324 deletions(-) delete mode 100644 mainnet-contracts/src/interface/IPufferModule.sol diff --git a/mainnet-contracts/.solhint.json b/mainnet-contracts/.solhint.json index 086e1e33..cab76070 100644 --- a/mainnet-contracts/.solhint.json +++ b/mainnet-contracts/.solhint.json @@ -6,7 +6,6 @@ "compiler-version": ["error", ">=0.8.0 <0.9.0"], "contract-name-camelcase": "off", "const-name-snakecase": "off", - "custom-errors": "error", "func-name-mixedcase": "off", "func-visibility": ["error", { "ignoreConstructors": true }], "max-line-length": ["error", 123], diff --git a/mainnet-contracts/package.json b/mainnet-contracts/package.json index 952de4d2..2ad96827 100644 --- a/mainnet-contracts/package.json +++ b/mainnet-contracts/package.json @@ -12,7 +12,6 @@ "@openzeppelin/contracts-upgradeable": "5.0.1", "l2-contracts": "*", "murky": "https://github.com/dmfxyz/murky.git", - "openzeppelin-foundry-upgrades": "https://github.com/bxmmm1/openzeppelin-foundry-upgrades.git#patch-1", "rave": "https://github.com/PufferFinance/rave.git#57ce268", "solidity-stringutils": "https://github.com/Arachnid/solidity-stringutils" }, @@ -20,9 +19,9 @@ "@crytic/properties": "https://github.com/crytic/properties#f1ff61b", "@prb/test": "0.6.4", "erc4626-tests": "https://github.com/a16z/erc4626-tests#8b1d7c2", - "forge-std": "github:foundry-rs/forge-std#v1.9.2", + "forge-std": "github:foundry-rs/forge-std#v1.9.4", "solarray": "github:evmcheb/solarray#a547630", - "solhint": "^5.0.1" + "solhint": "^5.0.3" }, "homepage": "https://puffer.fi", "keywords": [ diff --git a/mainnet-contracts/remappings.txt b/mainnet-contracts/remappings.txt index d43006e6..9b809e46 100644 --- a/mainnet-contracts/remappings.txt +++ b/mainnet-contracts/remappings.txt @@ -11,5 +11,4 @@ rave-test/=node_modules/rave/test/ murky/=node_modules/murky/src/ l2-contracts/=node_modules/l2-contracts/ mainnet-contracts/=node_modules/mainnet-contracts/ -openzeppelin-foundry-upgrades/=node_modules/openzeppelin-foundry-upgrades/src/ solidity-stringutils/=node_modules/solidity-stringutils diff --git a/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata2.s.sol b/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata2.s.sol index 714fa520..3ae541c8 100644 --- a/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata2.s.sol +++ b/mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata2.s.sol @@ -21,10 +21,9 @@ contract GenerateAccessManagerCalldata2 is Script { function run(address moduleManager) public pure returns (bytes memory) { bytes[] memory calldatas = new bytes[](1); - bytes4[] memory daoSelectors = new bytes4[](3); + bytes4[] memory daoSelectors = new bytes4[](2); daoSelectors[0] = PufferModuleManager.callSetClaimerFor.selector; - daoSelectors[1] = PufferModuleManager.callStartCheckpoint.selector; - daoSelectors[2] = PufferModuleManager.callSetProofSubmitter.selector; + daoSelectors[1] = PufferModuleManager.callSetProofSubmitter.selector; calldatas[0] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, moduleManager, daoSelectors, ROLE_ID_DAO diff --git a/mainnet-contracts/src/L1RewardManager.sol b/mainnet-contracts/src/L1RewardManager.sol index d4597cad..00420793 100644 --- a/mainnet-contracts/src/L1RewardManager.sol +++ b/mainnet-contracts/src/L1RewardManager.sol @@ -28,28 +28,21 @@ contract L1RewardManager is { /** * @notice The XPUFETH token contract on Ethereum Mainnet - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ IERC20 public immutable XPUFETH; /** * @notice The PufferVault contract on Ethereum Mainnet - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ PufferVaultV5 public immutable PUFFER_VAULT; /** * @notice The XERC20Lockbox contract on Ethereum Mainnet - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ IXERC20Lockbox public immutable LOCKBOX; /** * @notice The Rewards Manager contract on L2 - * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ address public immutable L2_REWARDS_MANAGER; - /** - * @custom:oz-upgrades-unsafe-allow constructor - */ constructor(address xPufETH, address lockbox, address pufETH, address l2RewardsManager) { XPUFETH = IERC20(xPufETH); LOCKBOX = IXERC20Lockbox(lockbox); diff --git a/mainnet-contracts/src/L1RewardManagerUnsafe.sol b/mainnet-contracts/src/L1RewardManagerUnsafe.sol index 1cf3c69a..5ed27c19 100644 --- a/mainnet-contracts/src/L1RewardManagerUnsafe.sol +++ b/mainnet-contracts/src/L1RewardManagerUnsafe.sol @@ -113,9 +113,6 @@ contract L1RewardManagerUnsafe is */ address public immutable L2_REWARDS_MANAGER; - /** - * @custom:oz-upgrades-unsafe-allow constructor - */ constructor(address xPufETH, address lockbox, address pufETH, address l2RewardsManager) { XPUFETH = IERC20(xPufETH); LOCKBOX = IXERC20Lockbox(lockbox); diff --git a/mainnet-contracts/src/PufferModule.sol b/mainnet-contracts/src/PufferModule.sol index f167e7ab..bec3d35e 100644 --- a/mainnet-contracts/src/PufferModule.sol +++ b/mainnet-contracts/src/PufferModule.sol @@ -10,7 +10,6 @@ import { IStrategy } from "../src/interface/Eigenlayer-Slashing/IStrategy.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { IEigenPod } from "../src/interface/Eigenlayer-Slashing/IEigenPod.sol"; import { PufferModuleManager } from "./PufferModuleManager.sol"; -import { IPufferModule } from "./interface/IPufferModule.sol"; import { Unauthorized } from "./Errors.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; @@ -25,54 +24,25 @@ import { IRewardsCoordinator } from "src/interface/Eigenlayer-Slashing/IRewardsC * @notice PufferModule * @custom:security-contact security@puffer.fi */ -contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable { +contract PufferModule is Initializable, AccessManagedUpgradeable { using Address for address; using Address for address payable; - /** - * @dev Represents the Beacon Chain strategy in EigenLayer - */ - address internal constant _BEACON_CHAIN_STRATEGY = 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0; - - /** - * @dev Upgradeable contract from EigenLayer - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IEigenPodManager public immutable EIGEN_POD_MANAGER; - - /** - * @dev Upgradeable contract from EigenLayer - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IRewardsCoordinator public immutable EIGEN_REWARDS_COORDINATOR; - - /** - * @dev Upgradeable contract from EigenLayer - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IDelegationManager public immutable EIGEN_DELEGATION_MANAGER; - - /** - * @dev Upgradeable PufferProtocol - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IPufferProtocol public immutable PUFFER_PROTOCOL; - + PufferModuleManager public immutable PUFFER_MODULE_MANAGER; /** - * @dev Upgradeable Puffer Module Manager - * @custom:oz-upgrades-unsafe-allow state-variable-immutable + * @dev Represents the Beacon Chain strategy in EigenLayer */ - PufferModuleManager public immutable PUFFER_MODULE_MANAGER; - + address internal constant _BEACON_CHAIN_STRATEGY = 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0; /** * keccak256(abi.encode(uint256(keccak256("PufferModule.storage")) - 1)) & ~bytes32(uint256(0xff)) */ bytes32 private constant _PUFFER_MODULE_BASE_STORAGE = 0x501caad7d5b9c1542c99d193b659cbf5c57571609bcfc93d65f1e159821d6200; - /** - * @custom:oz-upgrades-unsafe-allow constructor - */ constructor( IPufferProtocol protocol, address eigenPodManager, @@ -127,7 +97,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable receive() external payable { } /** - * @inheritdoc IPufferModule + * @notice Starts the validator */ function callStake(bytes calldata pubKey, bytes calldata signature, bytes32 depositDataRoot) external @@ -139,7 +109,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule + * @notice Sets the proof submitter on the EigenPod */ function setProofSubmitter(address proofSubmitter) external onlyPufferModuleManager { ModuleStorage storage $ = _getPufferModuleStorage(); @@ -148,8 +118,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule - * @dev Restricted to PufferModuleManager + * @notice Queues the withdrawal from EigenLayer for the Beacon Chain strategy */ function queueWithdrawals(uint256 shareAmount) external @@ -176,7 +145,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule + * @notice Completes the queued withdrawals */ function completeQueuedWithdrawals( IDelegationManagerTypes.Withdrawal[] calldata withdrawals, @@ -191,16 +160,9 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule - * @dev Restricted to PufferModuleManager - */ - function startCheckpoint() external virtual onlyPufferModuleManager { - ModuleStorage storage $ = _getPufferModuleStorage(); - $.eigenPod.startCheckpoint({ revertIfNoBalance: true }); - } - - /** - * @dev Restricted to PufferProtocol + * @notice the `to` with custom `value` and `data` + * @return success the success of the call + * @return returnData the return data of the call */ function call(address to, uint256 amount, bytes calldata data) external @@ -213,8 +175,10 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule - * @dev Restricted to PufferModuleManager + * @notice Calls the delegateTo function on the EigenLayer delegation manager + * @param operator is the address of the restaking operator + * @param approverSignatureAndExpiry the signature of the delegation approver + * @param approverSalt salt for the signature */ function callDelegateTo( address operator, @@ -225,23 +189,21 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule - * @dev Restricted to PufferModuleManager + * @notice Calls the undelegate function on the EigenLayer delegation manager */ function callUndelegate() external virtual onlyPufferModuleManager returns (bytes32[] memory withdrawalRoot) { return EIGEN_DELEGATION_MANAGER.undelegate(address(this)); } /** - * @inheritdoc IPufferModule - * @dev Restricted to PufferModuleManager + * @notice Sets the rewards claimer to `claimer` for the PufferModule */ function callSetClaimerFor(address claimer) external virtual onlyPufferModuleManager { EIGEN_REWARDS_COORDINATOR.setClaimerFor(claimer); } /** - * @inheritdoc IPufferModule + * @notice Returns the Withdrawal credentials for that module */ function getWithdrawalCredentials() public view returns (bytes memory) { // Withdrawal credentials for EigenLayer modules are EigenPods @@ -250,7 +212,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule + * @notice Returns the EigenPod address owned by the module */ function getEigenPod() external view returns (address) { ModuleStorage storage $ = _getPufferModuleStorage(); @@ -258,7 +220,7 @@ contract PufferModule is IPufferModule, Initializable, AccessManagedUpgradeable } /** - * @inheritdoc IPufferModule + * @notice Returns the module name */ // solhint-disable-next-line func-name-mixedcase function NAME() external view returns (bytes32) { diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index 6f243dc7..e84ae30e 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IPufferModule } from "./interface/IPufferModule.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; import { Unauthorized, InvalidAmount } from "./Errors.sol"; import { IPufferProtocol } from "./interface/IPufferProtocol.sol"; @@ -19,6 +18,7 @@ import { ISignatureUtils } from "../src/interface/Eigenlayer-Slashing/ISignature import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { RestakingOperator } from "./RestakingOperator.sol"; import { IAllocationManager } from "../src/interface/Eigenlayer-Slashing/IAllocationManager.sol"; +import { PufferModule } from "./PufferModule.sol"; /** * @title PufferModuleManager @@ -67,7 +67,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, ) external virtual restricted { address moduleAddress = IPufferProtocol(PUFFER_PROTOCOL).getModuleAddress(moduleName); - IPufferModule(moduleAddress).completeQueuedWithdrawals({ + PufferModule(payable(moduleAddress)).completeQueuedWithdrawals({ withdrawals: withdrawals, tokens: tokens, receiveAsTokens: receiveAsTokens @@ -89,20 +89,22 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, * @dev Restricted to the PufferProtocol * @param moduleName The name of the module */ - function createNewPufferModule(bytes32 moduleName) external virtual onlyPufferProtocol returns (IPufferModule) { + function createNewPufferModule(bytes32 moduleName) external virtual onlyPufferProtocol returns (PufferModule) { if (moduleName == bytes32("NO_VALIDATORS")) { revert ForbiddenModuleName(); } // This called from the PufferProtocol and the event is emitted there - return IPufferModule( - Create2.deploy({ - amount: 0, - salt: moduleName, - bytecode: abi.encodePacked( - type(BeaconProxy).creationCode, - abi.encode(PUFFER_MODULE_BEACON, abi.encodeCall(PufferModule.initialize, (moduleName, authority()))) - ) - }) + return PufferModule( + payable( + Create2.deploy({ + amount: 0, + salt: moduleName, + bytecode: abi.encodePacked( + type(BeaconProxy).creationCode, + abi.encode(PUFFER_MODULE_BEACON, abi.encodeCall(PufferModule.initialize, (moduleName, authority()))) + ) + }) + ) ); } @@ -119,7 +121,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, for (uint256 i = 0; i < modules.length; ++i) { //solhint-disable-next-line avoid-low-level-calls - (bool success,) = IPufferModule(modules[i]).call(address(this), rewardsAmounts[i], ""); + (bool success,) = PufferModule(payable(modules[i])).call(address(this), rewardsAmounts[i], ""); if (!success) { revert InvalidAmount(); } @@ -134,7 +136,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, */ function callQueueWithdrawals(bytes32 moduleName, uint256 sharesAmount) external virtual restricted { address moduleAddress = IPufferProtocol(PUFFER_PROTOCOL).getModuleAddress(moduleName); - bytes32[] memory withdrawalRoots = IPufferModule(moduleAddress).queueWithdrawals(sharesAmount); + bytes32[] memory withdrawalRoots = PufferModule(payable(moduleAddress)).queueWithdrawals(sharesAmount); emit WithdrawalsQueued(moduleName, sharesAmount, withdrawalRoots[0]); } @@ -142,8 +144,8 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, * @dev Restricted to the DAO */ function callSetClaimerFor(address moduleOrReOp, address claimer) external virtual restricted { - // We can cast `moduleOrReOp` to IPufferModule/RestakingOperator, uses the same function signature. - IPufferModule(moduleOrReOp).callSetClaimerFor(claimer); + // We can cast `moduleOrReOp` to PufferModule/RestakingOperator, uses the same function signature. + PufferModule(payable(moduleOrReOp)).callSetClaimerFor(claimer); emit ClaimerSet({ rewardsReceiver: moduleOrReOp, claimer: claimer }); } @@ -152,7 +154,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, */ function callSetProofSubmitter(bytes32 moduleName, address proofSubmitter) external virtual restricted { address moduleAddress = IPufferProtocol(PUFFER_PROTOCOL).getModuleAddress(moduleName); - IPufferModule(moduleAddress).setProofSubmitter(proofSubmitter); + PufferModule(payable(moduleAddress)).setProofSubmitter(proofSubmitter); emit ProofSubmitterSet(moduleName, proofSubmitter); } @@ -219,7 +221,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, ) external virtual restricted { address moduleAddress = IPufferProtocol(PUFFER_PROTOCOL).getModuleAddress(moduleName); - IPufferModule(moduleAddress).callDelegateTo(operator, approverSignatureAndExpiry, approverSalt); + PufferModule(payable(moduleAddress)).callDelegateTo(operator, approverSignatureAndExpiry, approverSalt); emit PufferModuleDelegated(moduleName, operator); } @@ -230,7 +232,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, function callUndelegate(bytes32 moduleName) external virtual restricted returns (bytes32[] memory withdrawalRoot) { address moduleAddress = IPufferProtocol(PUFFER_PROTOCOL).getModuleAddress(moduleName); - withdrawalRoot = IPufferModule(moduleAddress).callUndelegate(); + withdrawalRoot = PufferModule(payable(moduleAddress)).callUndelegate(); emit PufferModuleUndelegated(moduleName); } @@ -264,16 +266,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, emit CustomCallSucceeded(address(restakingOperator), target, customCalldata, response); } - /** - * @dev Restricted to the DAO - */ - function callStartCheckpoint(address[] calldata moduleAddresses) external virtual restricted { - for (uint256 i = 0; i < moduleAddresses.length; ++i) { - // reverts if supplied with a duplicate module address - IPufferModule(moduleAddresses[i]).startCheckpoint(); - } - } - /** * @dev Restricted to the DAO */ diff --git a/mainnet-contracts/src/PufferProtocol.sol b/mainnet-contracts/src/PufferProtocol.sol index fe8508ac..d910547e 100644 --- a/mainnet-contracts/src/PufferProtocol.sol +++ b/mainnet-contracts/src/PufferProtocol.sol @@ -10,7 +10,6 @@ import { PufferModuleManager } from "./PufferModuleManager.sol"; import { IPufferOracleV2 } from "./interface/IPufferOracleV2.sol"; import { IGuardianModule } from "./interface/IGuardianModule.sol"; import { IBeaconDepositContract } from "./interface/IBeaconDepositContract.sol"; -import { IPufferModule } from "./interface/IPufferModule.sol"; import { ValidatorKeyData } from "./struct/ValidatorKeyData.sol"; import { Validator } from "./struct/Validator.sol"; import { Permit } from "./structs/Permit.sol"; @@ -23,6 +22,7 @@ import { PufferVaultV5 } from "./PufferVaultV5.sol"; import { ValidatorTicket } from "./ValidatorTicket.sol"; import { InvalidAddress } from "./Errors.sol"; import { StoppedValidatorInfo } from "./struct/StoppedValidatorInfo.sol"; +import { PufferModule } from "./PufferModule.sol"; /** * @title PufferProtocol @@ -368,7 +368,8 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad uint256 transferAmount = validatorInfos[i].withdrawalAmount > 32 ether ? 32 ether : validatorInfos[i].withdrawalAmount; //solhint-disable-next-line avoid-low-level-calls - (bool success,) = IPufferModule(validatorInfos[i].module).call(address(PUFFER_VAULT), transferAmount, ""); + (bool success,) = + PufferModule(payable(validatorInfos[i].module)).call(address(PUFFER_VAULT), transferAmount, ""); if (!success) { revert Failed(); } @@ -572,7 +573,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad * @inheritdoc IPufferProtocol */ function getWithdrawalCredentials(address module) public view returns (bytes memory) { - return IPufferModule(module).getWithdrawalCredentials(); + return PufferModule(payable(module)).getWithdrawalCredentials(); } /** @@ -700,7 +701,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad if (address($.modules[moduleName]) != address(0)) { revert ModuleAlreadyExists(); } - IPufferModule module = PUFFER_MODULE_MANAGER.createNewPufferModule(moduleName); + PufferModule module = PUFFER_MODULE_MANAGER.createNewPufferModule(moduleName); $.modules[moduleName] = module; $.moduleWeights.push(moduleName); bytes32 withdrawalCredentials = bytes32(module.getWithdrawalCredentials()); @@ -789,7 +790,7 @@ contract PufferProtocol is IPufferProtocol, AccessManagedUpgradeable, UUPSUpgrad guardianEnclaveSignatures: guardianEnclaveSignatures }); - IPufferModule module = $.modules[moduleName]; + PufferModule module = $.modules[moduleName]; // Transfer 32 ETH to the module PUFFER_VAULT.transferETH(address(module), 32 ether); diff --git a/mainnet-contracts/src/PufferRevenueDepositor.sol b/mainnet-contracts/src/PufferRevenueDepositor.sol index 235f2077..915ebc71 100644 --- a/mainnet-contracts/src/PufferRevenueDepositor.sol +++ b/mainnet-contracts/src/PufferRevenueDepositor.sol @@ -32,28 +32,14 @@ contract PufferRevenueDepositor is * @notice The maximum rewards distribution window. */ uint256 private constant _MAXIMUM_DISTRIBUTION_WINDOW = 7 days; - - /** - * @notice PufferVault contract. - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ PufferVaultV5 public immutable PUFFER_VAULT; - - /** - * @notice AeraVault contract. - */ IAeraVault public immutable AERA_VAULT; - - /** - * @notice WETH contract. - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IWETH public immutable WETH; /** * @param vault PufferVault contract * @param weth WETH contract - * @custom:oz-upgrades-unsafe-allow constructor + * @param aeraVault AeraVault contract */ constructor(address vault, address weth, address aeraVault) { if (vault == address(0) || weth == address(0) || aeraVault == address(0)) { diff --git a/mainnet-contracts/src/PufferVault.sol b/mainnet-contracts/src/PufferVault.sol index b6f54e3f..293d16fb 100644 --- a/mainnet-contracts/src/PufferVault.sol +++ b/mainnet-contracts/src/PufferVault.sol @@ -31,21 +31,9 @@ contract PufferVault is { using EnumerableSet for EnumerableSet.UintSet; - /** - * @dev stETH contract - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IStETH internal immutable _ST_ETH; - - /** - * @dev Lido Withdrawal Queue - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ ILidoWithdrawalQueue internal immutable _LIDO_WITHDRAWAL_QUEUE; - /** - * @custom:oz-upgrades-unsafe-allow constructor - */ constructor(IStETH stETH, ILidoWithdrawalQueue lidoWithdrawalQueue) payable { _ST_ETH = stETH; _LIDO_WITHDRAWAL_QUEUE = lidoWithdrawalQueue; diff --git a/mainnet-contracts/src/PufferVaultV2.sol b/mainnet-contracts/src/PufferVaultV2.sol index 28bc0f89..e0adaad8 100644 --- a/mainnet-contracts/src/PufferVaultV2.sol +++ b/mainnet-contracts/src/PufferVaultV2.sol @@ -24,34 +24,9 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { using Math for uint256; uint256 private constant _BASIS_POINT_SCALE = 1e4; - - /** - * @dev The Wrapped Ethereum ERC20 token - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IWETH internal immutable _WETH; - - /** - * @dev The PufferOracle contract - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ IPufferOracle public immutable PUFFER_ORACLE; - /** - * @dev Two wallets that transferred pufETH to the PufferVault by mistake. - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ - address private constant _WHALE_PUFFER = 0xe6957D9b493b2f2634c8898AC09dc14Cb24BE222; - - /** - * @dev Two wallets that transferred pufETH to the PufferVault by mistake. - * @custom:oz-upgrades-unsafe-allow state-variable-immutable - */ - address private constant _PUFFER = 0x34c912C13De7953530DBE4c32F597d1bAF77889b; - - /** - * @custom:oz-upgrades-unsafe-allow constructor - */ constructor(IStETH stETH, IWETH weth, ILidoWithdrawalQueue lidoWithdrawalQueue, IPufferOracle oracle) PufferVault(stETH, lidoWithdrawalQueue) { @@ -74,19 +49,6 @@ contract PufferVaultV2 is PufferVault, IPufferVaultV2 { ERC4626Storage storage erc4626Storage = _getERC4626StorageInternal(); erc4626Storage._asset = _WETH; _setExitFeeBasisPoints(100); // 1% - - // Return pufETH to Puffers - // If statement is necessary because we don't wan to change existing tests that rely on the original behavior - if (balanceOf(address(this)) > 299 ether) { - // Must do this.transfer (external call) because ERC20Upgradeable uses Context::_msgSender() (the msg.sender of the .initialize external call) - - // https://etherscan.io/tx/0x2e02a00dbc8ba48cd65a6802d174c210d0c4869806a564cca0088e42d382b2ff - // slither-disable-next-line unchecked-transfer - this.transfer(_WHALE_PUFFER, 299.864287100672938618 ether); - // https://etherscan.io/tx/0x7d309dc26cb3f0226e480e0d4c598707faee59d58bfc68bedb75cf5055ac274a - // slither-disable-next-line unchecked-transfer - this.transfer(_PUFFER, 25426113577506618); - } } /** diff --git a/mainnet-contracts/src/interface/IPufferModule.sol b/mainnet-contracts/src/interface/IPufferModule.sol deleted file mode 100644 index 4d72e9a3..00000000 --- a/mainnet-contracts/src/interface/IPufferModule.sol +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.0 <0.9.0; - -import { ISignatureUtils } from "../interface/Eigenlayer-Slashing/ISignatureUtils.sol"; -// import { BeaconChainProofs } from "../interface/Eigenlayer-Slashing/BeaconChainProofs.sol"; -import { IDelegationManager } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IDelegationManagerTypes } from "../interface/Eigenlayer-Slashing/IDelegationManager.sol"; - -/** - * @title IPufferModule - * @author Puffer Finance - * @custom:security-contact security@puffer.fi - */ -interface IPufferModule { - /** - * @notice Thrown if the rewards are already claimed for a `blockNumber` - * @dev Signature "0xa9214540" - */ - error AlreadyClaimed(uint256 blockNumber, address node); - - /** - * @notice Thrown if guardians try to post root for an invalid block number - * @dev Signature "0x9f4aafbe" - */ - error InvalidBlockNumber(uint256 blockNumber); - - /** - * @notice Thrown if the there is nothing to be claimed for the provided information - * @dev Signature "0x64ab3466" - */ - error NothingToClaim(address node); - - /** - * @notice Emitted when the rewards MerkleRoot `root` for a `blockNumber` is posted - */ - event RewardsRootPosted(uint256 indexed blockNumber, bytes32 root); - - /** - * @notice Emits when rewards are claimed - * @param node is the node address - * @param amount is the amount claimed in wei - */ - event RewardsClaimed(address indexed node, uint256 amount); - - /** - * @notice Returns the Withdrawal credentials for that module - */ - function getWithdrawalCredentials() external view returns (bytes memory); - - /** - * @notice Returns the module name - */ - function NAME() external view returns (bytes32); - - /** - * @notice Starts the validator - */ - function callStake(bytes calldata pubKey, bytes calldata signature, bytes32 depositDataRoot) external payable; - - /** - * @notice Sets the proof submitter on the EigenPod - */ - function setProofSubmitter(address proofSubmitter) external; - - /** - * @notice Calls the delegateTo function on the EigenLayer delegation manager - * @param operator is the address of the restaking operator - * @param approverSignatureAndExpiry the signature of the delegation approver - * @param approverSalt salt for the signature - * @dev Restricted to the DAO - */ - function callDelegateTo( - address operator, - ISignatureUtils.SignatureWithExpiry calldata approverSignatureAndExpiry, - bytes32 approverSalt - ) external; - - /** - * @notice Calls the undelegate function on the EigenLayer delegation manager - * @dev Restricted to the DAO - */ - function callUndelegate() external returns (bytes32[] memory withdrawalRoot); - - /** - * @notice Returns the EigenPod address owned by the module - */ - function getEigenPod() external view returns (address); - - /** - * @notice Queues the withdrawal from EigenLayer for the Beacon Chain strategy - * @dev Restricted to PufferModuleManager - */ - function queueWithdrawals(uint256 shareAmount) external returns (bytes32[] memory); - - /** - * @notice Completes the queued withdrawals - */ - function completeQueuedWithdrawals( - IDelegationManagerTypes.Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - bool[] calldata receiveAsTokens - ) external; - - /** - * @notice Starts the checkpoint for the module - */ - function startCheckpoint() external; - - /** - * @notice Function callable only by PufferProtocol - * @param to is the destination address - * @param amount is the ETH amount in wei - * @param data is the calldata - */ - function call(address to, uint256 amount, bytes calldata data) - external - returns (bool success, bytes memory response); - - /** - * @notice Sets the rewards claimer to `claimer` for the PufferModule - */ - function callSetClaimerFor(address claimer) external; -} diff --git a/mainnet-contracts/src/struct/ProtocolStorage.sol b/mainnet-contracts/src/struct/ProtocolStorage.sol index 78a6af02..c87d18e2 100644 --- a/mainnet-contracts/src/struct/ProtocolStorage.sol +++ b/mainnet-contracts/src/struct/ProtocolStorage.sol @@ -3,8 +3,7 @@ pragma solidity >=0.8.0 <0.9.0; import { Validator } from "../struct/Validator.sol"; import { NodeInfo } from "../struct/NodeInfo.sol"; -import { IPufferModule } from "../interface/IPufferModule.sol"; - +import { PufferModule } from "../PufferModule.sol"; /** * @custom:storage-location erc7201:PufferProtocol.storage * @dev +-----------------------------------------------------------+ @@ -13,6 +12,7 @@ import { IPufferModule } from "../interface/IPufferModule.sol"; * | | * +-----------------------------------------------------------+ */ + struct ProtocolStorage { /** * @dev Module weights @@ -44,7 +44,7 @@ struct ProtocolStorage { * @dev Mapping between module name and a module * Slot 5 */ - mapping(bytes32 moduleName => IPufferModule moduleAddress) modules; + mapping(bytes32 moduleName => PufferModule moduleAddress) modules; /** * @dev Mapping of Module name => Module limit * Slot 6 diff --git a/mainnet-contracts/test/handlers/PufferProtocolHandler.sol b/mainnet-contracts/test/handlers/PufferProtocolHandler.sol index fe5a0c66..3609495c 100644 --- a/mainnet-contracts/test/handlers/PufferProtocolHandler.sol +++ b/mainnet-contracts/test/handlers/PufferProtocolHandler.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0 <0.9.0; -import { IPufferModule } from "../../src/interface/IPufferModule.sol"; import { IPufferProtocol } from "../../src/interface/IPufferProtocol.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; @@ -27,6 +26,7 @@ import { IValidatorTicket } from "../../src/interface/IValidatorTicket.sol"; import { PufferOracleV2 } from "../../src/PufferOracleV2.sol"; import { IWETH } from "../../src/interface/Other/IWETH.sol"; import { PufferVaultV5 } from "../../src/PufferVaultV5.sol"; +import { PufferModule } from "../../src/PufferModule.sol"; struct ProvisionedValidator { bytes32 moduleName; @@ -693,7 +693,7 @@ contract PufferProtocolHandler is Test { function _enableCall(address module) internal { // Enable PufferProtocol to call `call` function on module bytes4[] memory selectors = new bytes4[](1); - selectors[0] = IPufferModule.call.selector; + selectors[0] = PufferModule.call.selector; vm.startPrank(_accessManagerAdmin); AccessManager(pufferProtocol.authority()).setTargetFunctionRole(module, selectors, ROLE_ID_PUFFER_PROTOCOL); vm.stopPrank(); diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index 4bf0d6d5..a49b3d12 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -280,20 +280,6 @@ contract PufferModuleManagerTest is UnitTestHelper { pufferModuleManager.callSetProofSubmitter(moduleName, proofSubmitter); } - function test_startCheckpoint(bytes32 moduleName) public { - vm.assume(pufferProtocol.getModuleAddress(moduleName) == address(0)); - - vm.startPrank(DAO); - - address module = _createPufferModule(moduleName); - address[] memory modules = new address[](1); - modules[0] = module; - - vm.startPrank(DAO); - - pufferModuleManager.callStartCheckpoint(modules); - } - function testRevert_createPufferModuleForbiddenName() public { vm.startPrank(DAO); vm.expectRevert(IPufferModuleManager.ForbiddenModuleName.selector); diff --git a/mainnet-contracts/test/unit/PufferProtocol.t.sol b/mainnet-contracts/test/unit/PufferProtocol.t.sol index 192befdf..66e08329 100644 --- a/mainnet-contracts/test/unit/PufferProtocol.t.sol +++ b/mainnet-contracts/test/unit/PufferProtocol.t.sol @@ -10,7 +10,6 @@ import { Status } from "../../src/struct/Status.sol"; import { Validator } from "../../src/struct/Validator.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; import { PufferModule } from "../../src/PufferModule.sol"; -import { IPufferModule } from "../../src/interface/IPufferModule.sol"; import { ROLE_ID_DAO, ROLE_ID_OPERATIONS_PAYMASTER, ROLE_ID_OPERATIONS_MULTISIG } from "../../script/Roles.sol"; import { Unauthorized } from "../../src/Errors.sol"; import { LibGuardianMessages } from "../../src/LibGuardianMessages.sol"; @@ -502,7 +501,7 @@ contract PufferProtocolTest is UnitTestHelper { function test_create_puffer_module() public { bytes32 name = bytes32("LEVERAGED_RESTAKING"); pufferProtocol.createPufferModule(name); - IPufferModule module = IPufferModule(pufferProtocol.getModuleAddress(name)); + PufferModule module = PufferModule(payable(pufferProtocol.getModuleAddress(name))); assertEq(module.NAME(), name, "name"); } diff --git a/yarn.lock b/yarn.lock index 3b33d90b..7c5bd46a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -704,6 +704,10 @@ for-each@^0.3.3: version "1.9.2" resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/1714bee72e286e73f76e320d110e0eaf5c4e649d" +"forge-std@github:foundry-rs/forge-std#v1.9.4": + version "1.9.4" + resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/1eea5bae12ae557d589f9f0f0edae2faa47cb262" + form-data-encoder@^2.1.2: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" @@ -1338,10 +1342,6 @@ once@^1.3.0: dependencies: wrappy "1" -"openzeppelin-foundry-upgrades@https://github.com/bxmmm1/openzeppelin-foundry-upgrades.git#patch-1": - version "0.3.3" - resolved "https://github.com/bxmmm1/openzeppelin-foundry-upgrades.git#faa33a1767287741288cf2b1f68ae0e48a8befc7" - p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" @@ -1671,6 +1671,32 @@ solhint@^5.0.1: optionalDependencies: prettier "^2.8.3" +solhint@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-5.0.3.tgz#b57f6d2534fe09a60f9db1b92e834363edd1cbde" + integrity sha512-OLCH6qm/mZTCpplTXzXTJGId1zrtNuDYP5c2e6snIv/hdRVxPfBBz/bAlL91bY/Accavkayp2Zp2BaDSrLVXTQ== + dependencies: + "@solidity-parser/parser" "^0.18.0" + ajv "^6.12.6" + antlr4 "^4.13.1-patch-1" + ast-parents "^0.0.1" + chalk "^4.1.2" + commander "^10.0.0" + cosmiconfig "^8.0.0" + fast-diff "^1.2.0" + glob "^8.0.3" + ignore "^5.2.4" + js-yaml "^4.1.0" + latest-version "^7.0.0" + lodash "^4.17.21" + pluralize "^8.0.0" + semver "^7.5.2" + strip-ansi "^6.0.1" + table "^6.8.1" + text-table "^0.2.0" + optionalDependencies: + prettier "^2.8.3" + solidity-ast@^0.4.38: version "0.4.56" resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.56.tgz#94fe296f12e8de1a3bed319bc06db8d05a113d7a" From f252525766a6c10fc9b35c90647cb938369ba77a Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 18 Dec 2024 14:44:46 +0100 Subject: [PATCH 27/37] add VaultV5 doc --- mainnet-contracts/docs/PufferVaultV5.md | 184 ++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 mainnet-contracts/docs/PufferVaultV5.md diff --git a/mainnet-contracts/docs/PufferVaultV5.md b/mainnet-contracts/docs/PufferVaultV5.md new file mode 100644 index 00000000..c1ed8a75 --- /dev/null +++ b/mainnet-contracts/docs/PufferVaultV5.md @@ -0,0 +1,184 @@ +# PufferVault V5 + +The PufferVaultV5 contract is the latest upgrade to the Puffer Vault, responsible for custodying funds for the Puffer protocol. It inherits from PufferVaultStorage and implements various interfaces including IPufferVaultV5, IERC721Receiver, AccessManagedUpgradeable, ERC20PermitUpgradeable, ERC4626Upgradeable, and UUPSUpgradeable. + +The PufferVaultV5 contract is a modified ERC4626 vault with custom logic to handle deposits of stETH and ETH in addition to the standard WETH. It extends the ERC4626Upgradeable contract and overrides key functions like `deposit`, `mint`, `withdraw`, and `redeem` to support these additional asset types. The vault tracks the total assets across ETH, stETH, and WETH balances, and handles conversions between these asset types as needed. It also integrates with Lido for stETH withdrawals. Overall, PufferVaultV5 provides a custodial vault tailored for the Puffer protocol's unique requirements while adhering to the ERC4626 standard for compatibility. + +## Core Functionality + +The vault manages deposits and withdrawals of ETH, stETH, and WETH while minting pufETH tokens to represent user shares. It handles: + +- WETH/ETH/stETH deposits and conversions to pufETH +- Withdrawals of WETH in exchange for pufETH +- Reward distribution and management +- Integration with Lido for stETH withdrawals +- Integration with EigenLayer for restaking + +## Key Components + +### Asset Management +- Supports native ETH, stETH, and WETH +- Tracks total assets across multiple forms (ETH, stETH, WETH) +- Handles conversions between asset types +- Manages reward distributions and deposits + +### Important State Variables +- `_ST_ETH`: Lido's stETH contract address +- `_LIDO_WITHDRAWAL_QUEUE`: Lido's withdrawal queue contract +- `_WETH`: Wrapped ETH contract +- `PUFFER_ORACLE`: Oracle for proof-of-reserves +- `RESTAKING_REWARDS_DEPOSITOR`: Contract for depositing rewards + +## Core Functions + +### Deposit Functions + +#### `depositETH` +Allows users to deposit native ETH and receive pufETH tokens in return. + +#### `depositStETH` +Enables deposits of stETH shares in exchange for pufETH tokens. + +#### `mint` +```solidity +function mint(uint256 shares, address receiver) public returns (uint256) +``` +Mints `shares` pufETH tokens and transfers them to `receiver`. Standard ERC4626 mint function. + +#### `deposit` +```solidity +function deposit(uint256 assets, address receiver) public returns (uint256) +``` +Deposits `assets` (WETH) and mints the corresponding amount of pufETH tokens to `receiver`. Standard ERC4626 deposit function. + +### Withdrawal Functions + +#### `withdraw` +```solidity +function withdraw(uint256 assets, address receiver, address owner) public returns (uint256) +``` +Withdraws WETH assets from the vault by burning pufETH shares. Standard ERC4626 withdraw function. + +#### `redeem` +```solidity +function redeem(uint256 shares, address receiver, address owner) public returns (uint256) +``` +Redeems pufETH shares for WETH assets. Standard ERC4626 redeem function. + +### Reward Management + +#### `mintRewards` +```solidity +function mintRewards(uint256 rewardsAmount) external returns (uint256 ethToPufETHRate, uint256 pufETHAmount) +``` +Mints pufETH rewards for the L1RewardManager contract. The rewards are then bridged to Base. On Base the Node operators can claim the rewards. + +#### `depositRewards` +```solidity +function depositRewards() external payable +``` +Deposits rewards to the vault and updates total reward deposit amount. + +### Lido Integration + +#### `initiateETHWithdrawalsFromLido` +```solidity +function initiateETHWithdrawalsFromLido(uint256[] calldata amounts) external returns (uint256[] memory) +``` +Initiates ETH withdrawals from Lido by queueing withdrawal requests. + +#### `claimWithdrawalsFromLido` +```solidity +function claimWithdrawalsFromLido(uint256[] calldata requestIds) external +``` +Claims completed ETH withdrawals from Lido. + +### Asset Transfer + +#### `transferETH` +```solidity +function transferETH(address to, uint256 ethAmount) external +``` +Transfers ETH to PufferModules for validator funding. + +### Fee Management + +#### `setExitFeeBasisPoints` +```solidity +function setExitFeeBasisPoints(uint256 newExitFeeBasisPoints) external +``` +Sets the exit fee basis points (max 2%). This exit fee is distributed to all pufETH holders. + +### View Functions + +#### `totalAssets` +```solidity +function totalAssets() public view returns (uint256) +``` +Calculates total assets by summing: +- WETH balance +- ETH balance +- Oracle-reported locked ETH +- Total reward mint amount +- Minus pending distributions and deposits + +#### `getPendingLidoETHAmount` +```solidity +function getPendingLidoETHAmount() public view returns (uint256) +``` +Returns amount of ETH pending withdrawal from Lido. + +#### `getTotalRewardMintAmount` +```solidity +function getTotalRewardMintAmount() public view returns (uint256) +``` +Returns total minted rewards amount. + +#### `getTotalRewardDepositAmount` +```solidity +function getTotalRewardDepositAmount() public view returns (uint256) +``` +Returns total deposited rewards amount. + +## Events + +### `UpdatedTotalRewardsAmount` +```solidity +event UpdatedTotalRewardsAmount(uint256 previousTotalRewardsAmount, uint256 newTotalRewardsAmount, uint256 depositedETHAmount) +``` +Emitted when rewards are deposited to the vault. + +### `RequestedWithdrawals` +```solidity +event RequestedWithdrawals(uint256[] requestIds) +``` +Emitted when withdrawals are requested from Lido. + +### `ClaimedWithdrawals` +```solidity +event ClaimedWithdrawals(uint256[] requestIds) +``` +Emitted when withdrawals are claimed from Lido. + +### `TransferredETH` +```solidity +event TransferredETH(address to, uint256 amount) +``` +Emitted when ETH is transferred to a PufferModule. + +## Security Features + +- Access control via AccessManagedUpgradeable +- Deposit tracking to prevent simultaneous deposits/withdrawals +- Maximum exit fee of 2% +- Upgradeable via UUPS pattern +- Secure ETH handling with fallback functions + +## Integration Points + +- EigenLayer for restaking +- Lido for stETH operations +- Puffer Oracle for proof-of-reserves on Beacon Chain +- Revenue Depositor for reward distribution + +The contract serves as the core vault for the Puffer protocol, managing user deposits and withdrawals. From aa26cb41d5f660833b7b1e85072ea287a9be2e81 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 18 Dec 2024 15:44:43 +0100 Subject: [PATCH 28/37] remove more useless stuff --- mainnet-contracts/docs/PufferVaultV5.md | 7 ---- .../07_GenerateSlashingELCalldata.s.sol | 8 ++-- mainnet-contracts/script/SetupAccess.s.sol | 16 ++++---- mainnet-contracts/src/PufferModuleManager.sol | 35 +++------------- mainnet-contracts/src/RestakingOperator.sol | 27 +++--------- .../src/interface/IPufferModuleManager.sol | 41 ++----------------- .../PufferModuleManager.integration.t.sol | 30 -------------- .../test/unit/PufferModuleManager.t.sol | 33 --------------- 8 files changed, 25 insertions(+), 172 deletions(-) diff --git a/mainnet-contracts/docs/PufferVaultV5.md b/mainnet-contracts/docs/PufferVaultV5.md index c1ed8a75..380db428 100644 --- a/mainnet-contracts/docs/PufferVaultV5.md +++ b/mainnet-contracts/docs/PufferVaultV5.md @@ -12,16 +12,9 @@ The vault manages deposits and withdrawals of ETH, stETH, and WETH while minting - Withdrawals of WETH in exchange for pufETH - Reward distribution and management - Integration with Lido for stETH withdrawals -- Integration with EigenLayer for restaking ## Key Components -### Asset Management -- Supports native ETH, stETH, and WETH -- Tracks total assets across multiple forms (ETH, stETH, WETH) -- Handles conversions between asset types -- Manages reward distributions and deposits - ### Important State Variables - `_ST_ETH`: Lido's stETH contract address - `_LIDO_WITHDRAWAL_QUEUE`: Lido's withdrawal queue contract diff --git a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol index 358299a5..d2fa8592 100644 --- a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol +++ b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol @@ -11,11 +11,9 @@ contract GenerateSlashingELCalldata is Script { function run(address pufferModuleManagerProxy) public pure returns (bytes memory) { bytes[] memory calldatas = new bytes[](1); - bytes4[] memory daoSelectors = new bytes4[](4); - daoSelectors[0] = PufferModuleManager.callModifyOperatorDetails.selector; - daoSelectors[1] = PufferModuleManager.callUpdateMetadataURI.selector; - daoSelectors[2] = PufferModuleManager.callRegisterOperatorToAVS.selector; - daoSelectors[3] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; + bytes4[] memory daoSelectors = new bytes4[](2); + daoSelectors[0] = PufferModuleManager.callRegisterOperatorToAVS.selector; + daoSelectors[1] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; calldatas[0] = abi.encodeCall(AccessManager.setTargetFunctionRole, (pufferModuleManagerProxy, daoSelectors, ROLE_ID_DAO)); diff --git a/mainnet-contracts/script/SetupAccess.s.sol b/mainnet-contracts/script/SetupAccess.s.sol index 4ad40bea..f4678d80 100644 --- a/mainnet-contracts/script/SetupAccess.s.sol +++ b/mainnet-contracts/script/SetupAccess.s.sol @@ -149,16 +149,14 @@ contract SetupAccess is BaseScript { bytes[] memory calldatas = new bytes[](2); // Dao selectors - bytes4[] memory selectors = new bytes4[](9); + bytes4[] memory selectors = new bytes4[](7); selectors[0] = PufferModuleManager.createNewRestakingOperator.selector; - selectors[1] = PufferModuleManager.callModifyOperatorDetails.selector; - selectors[2] = PufferModuleManager.callUpdateMetadataURI.selector; - selectors[3] = PufferModuleManager.callUndelegate.selector; - selectors[4] = PufferModuleManager.callDelegateTo.selector; - selectors[5] = PufferModuleManager.updateAVSRegistrationSignatureProof.selector; - selectors[6] = PufferModuleManager.callRegisterOperatorToAVS.selector; - selectors[7] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; - selectors[8] = PufferModuleManager.customExternalCall.selector; + selectors[1] = PufferModuleManager.callUndelegate.selector; + selectors[2] = PufferModuleManager.callDelegateTo.selector; + selectors[3] = PufferModuleManager.updateAVSRegistrationSignatureProof.selector; + selectors[4] = PufferModuleManager.callRegisterOperatorToAVS.selector; + selectors[5] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; + selectors[6] = PufferModuleManager.customExternalCall.selector; calldatas[0] = abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, pufferDeployment.moduleManager, selectors, ROLE_ID_DAO diff --git a/mainnet-contracts/src/PufferModuleManager.sol b/mainnet-contracts/src/PufferModuleManager.sol index e84ae30e..5a81a3f5 100644 --- a/mainnet-contracts/src/PufferModuleManager.sol +++ b/mainnet-contracts/src/PufferModuleManager.sol @@ -57,6 +57,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, /** * @notice Completes queued withdrawals + * * @dev Restricted to Puffer Paymaster */ function callCompleteQueuedWithdrawals( @@ -86,7 +87,6 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, } /** - * @dev Restricted to the PufferProtocol * @param moduleName The name of the module */ function createNewPufferModule(bytes32 moduleName) external virtual onlyPufferProtocol returns (PufferModule) { @@ -110,6 +110,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, /** * @notice Transfers the unlocked rewards from the modules to the vault + * * @dev Restricted to Puffer Paymaster */ function transferRewardsToTheVault(address[] calldata modules, uint256[] calldata rewardsAmounts) @@ -161,7 +162,7 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, /** * @dev Restricted to the DAO */ - function createNewRestakingOperator(string calldata metadataURI, address delegationApprover, uint32 allocationDelay) + function createNewRestakingOperator(string calldata metadataURI, uint32 allocationDelay) external virtual restricted @@ -174,42 +175,16 @@ contract PufferModuleManager is IPufferModuleManager, AccessManagedUpgradeable, type(BeaconProxy).creationCode, abi.encode( RESTAKING_OPERATOR_BEACON, - abi.encodeCall( - RestakingOperator.initialize, (authority(), delegationApprover, metadataURI, allocationDelay) - ) + abi.encodeCall(RestakingOperator.initialize, (authority(), metadataURI, allocationDelay)) ) ) }); - emit RestakingOperatorCreated(restakingOperator, delegationApprover); + emit RestakingOperatorCreated(restakingOperator); return RestakingOperator(restakingOperator); } - /** - * @dev Restricted to the DAO - */ - function callModifyOperatorDetails(RestakingOperator restakingOperator, address newDelegationApprover) - external - virtual - restricted - { - restakingOperator.modifyOperatorDetails(newDelegationApprover); - emit RestakingOperatorModified(address(restakingOperator), newDelegationApprover); - } - - /** - * @dev Restricted to the DAO - */ - function callUpdateMetadataURI(RestakingOperator restakingOperator, string calldata metadataURI) - external - virtual - restricted - { - restakingOperator.updateOperatorMetadataURI(metadataURI); - emit RestakingOperatorMetadataURIUpdated(address(restakingOperator), metadataURI); - } - /** * @dev Restricted to the DAO */ diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index 306ebec4..668f6102 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -97,28 +97,13 @@ contract RestakingOperator is IERC1271, Initializable, AccessManagedUpgradeable _disableInitializers(); } - function initialize( - address initialAuthority, - address initDelegationApprover, - string calldata metadataURI, - uint32 allocationDelay - ) external initializer { + function initialize(address initialAuthority, string calldata metadataURI, uint32 allocationDelay) + external + initializer + { __AccessManaged_init(initialAuthority); - EIGEN_DELEGATION_MANAGER.registerAsOperator(initDelegationApprover, allocationDelay, metadataURI); - } - - /** - * @dev Restricted to the PufferModuleManager - */ - function modifyOperatorDetails(address newDelegationApprover) external virtual onlyPufferModuleManager { - EIGEN_DELEGATION_MANAGER.modifyOperatorDetails(address(this), newDelegationApprover); - } - - /** - * @dev Restricted to the PufferModuleManager - */ - function updateOperatorMetadataURI(string calldata metadataURI) external virtual onlyPufferModuleManager { - EIGEN_DELEGATION_MANAGER.updateOperatorMetadataURI(address(this), metadataURI); + // Delegation approve is address(0) because we want everybody to be able to delegate to us + EIGEN_DELEGATION_MANAGER.registerAsOperator(address(0), allocationDelay, metadataURI); } /** diff --git a/mainnet-contracts/src/interface/IPufferModuleManager.sol b/mainnet-contracts/src/interface/IPufferModuleManager.sol index e9d72116..cd28b46d 100644 --- a/mainnet-contracts/src/interface/IPufferModuleManager.sol +++ b/mainnet-contracts/src/interface/IPufferModuleManager.sol @@ -2,12 +2,12 @@ pragma solidity >=0.8.0 <0.9.0; import { RestakingOperator } from "src/RestakingOperator.sol"; + /** * @title IPufferModuleManager * @author Puffer Finance * @custom:security-contact security@puffer.fi */ - interface IPufferModuleManager { /** * @notice Thrown if the module name is not allowed @@ -43,29 +43,12 @@ interface IPufferModuleManager { address indexed restakingOperator, address indexed avs, uint32[] operatorSetId ); - /** - * @notice Emitted when a Restaking Operator is opted into a slasher - * @param restakingOperator is the address of the restaking operator - * @param slasher is the address of the slasher contract - * @dev Signature "0xfaf85fa92e9a913f582def722d9da998852ef6cd2fc7715266e3c3b16495c7ac" - */ - event RestakingOperatorOptedInSlasher(address indexed restakingOperator, address indexed slasher); - /** * @notice Emitted when the Restaking Operator is created * @param restakingOperator is the address of the restaking operator - * @param delegationApprover is the address of the delegation approver - * @dev Signature "0x28682dddd8aa82d42ec7143a18beba2d09b27d4581f2f26a6afcd0da4576ae71" - */ - event RestakingOperatorCreated(address indexed restakingOperator, address indexed delegationApprover); - - /** - * @notice Emitted when the Restaking Operator is modified - * @param restakingOperator is the address of the restaking operator - * @param newOperatorDetails is the struct with new operator details - * @dev Signature "0xee78237d6444cc6c9083c1ef31a82b0feac23fbdf0cf52d7b0ed66dfa5f7f9f2" + * @dev Signature "0xc7178e96e72aa500a37cafe2999b91040f28d3d3a83e64eb3b6166345e804291" */ - event RestakingOperatorModified(address indexed restakingOperator, address indexed newOperatorDetails); + event RestakingOperatorCreated(address indexed restakingOperator); /** * @notice Emitted when the Withdrawals are queued @@ -75,14 +58,6 @@ interface IPufferModuleManager { */ event WithdrawalsQueued(bytes32 indexed moduleName, uint256 shareAmount, bytes32 withdrawalRoot); - /** - * @notice Emitted when the Restaking Operator is updated with a new metadata URI - * @param restakingOperator is the address of the restaking operator - * @param metadataURI is the new URI of the operator's metadata - * @dev Signature "0x4cb1b839d29c7a6f051ae51c7b439f2f8f991de54a4b5906503a06a0892ba2c4" - */ - event RestakingOperatorMetadataURIUpdated(address indexed restakingOperator, string metadataURI); - /** * @notice Emitted when the Puffer Module is delegated * @param moduleName the module name to be delegated @@ -107,14 +82,6 @@ interface IPufferModuleManager { */ event AVSRegistrationSignatureProofUpdated(address indexed restakingOperator, bytes32 digestHash, address signer); - /** - * @notice Emitted when a Node Operator verifies withdrawal credentials - * @param moduleName is the name of the module - * @param validatorIndices is the indices of the validators - * @dev Signature "0x6722c9fd02a30e38d993af1ef931e54d0c24d0eae5eba68982773ce120b8ddee" - */ - event ValidatorCredentialsVerified(bytes32 indexed moduleName, uint40[] validatorIndices); - /** * @notice Emitted when the withdrawals are completed * @param moduleName is the name of the module @@ -132,7 +99,7 @@ interface IPufferModuleManager { event ProofSubmitterSet(bytes32 indexed moduleName, address indexed proofSubmitter); /** - * @notice Emitted when the Restaking Operator or PufferModule sets the calimer to `claimer` + * @notice Emitted when the Restaking Operator or PufferModule sets the claimer to `claimer` * @dev Signature "0x4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a28" */ event ClaimerSet(address indexed rewardsReceiver, address indexed claimer); diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol index ddd33fca..7fd56baf 100644 --- a/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferModuleManager.integration.t.sol @@ -5,7 +5,6 @@ import "forge-std/console.sol"; import { IntegrationTestHelper } from "../helpers/IntegrationTestHelper.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; import { PufferProtocol } from "../../src/PufferProtocol.sol"; -import { IPufferModuleManager } from "../../src/interface/IPufferModuleManager.sol"; import { RestakingOperator } from "../../src/RestakingOperator.sol"; import { DeployEverything } from "script/DeployEverything.s.sol"; import { ISignatureUtils } from "../../src/interface/Eigenlayer-Slashing/ISignatureUtils.sol"; @@ -40,34 +39,6 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { pufferProtocol.createPufferModule(bytes32("SOME_MODULE_NAME")); } - // function test_modify_operator() public { - // vm.startPrank(DAO); - // RestakingOperator operator = _createRestakingOperator(); - - // address newDelegationApprover = makeAddr("newDelegationApprover"); - - // vm.expectEmit(true, true, true, true); - // emit IPufferModuleManager.RestakingOperatorModified(address(operator), newDelegationApprover); - // moduleManager.callModifyOperatorDetails({ - // restakingOperator: operator, - // newDelegationApprover: newDelegationApprover - // }); - - // address result = operator.EIGEN_DELEGATION_MANAGER().delegationApprover(address(operator)); - // assertEq(result, newDelegationApprover, "updated delegation approver"); - // } - - // function test_update_metadata_uri() public { - // vm.startPrank(DAO); - // RestakingOperator operator = _createRestakingOperator(); - - // string memory newUri = "https://puffer.fi/updated.json"; - - // vm.expectEmit(true, true, true, true); - // emit IPufferModuleManager.RestakingOperatorMetadataURIUpdated(address(operator), newUri); - // moduleManager.callUpdateMetadataURI(operator, newUri); - // } - function _depositToWETHEigenLayerStrategyAndDelegateTo(address restakingOperator) internal { // buy weth vm.startPrank(0xA85Fdcb45aaFF3C310a47FE309D4a35FAfbdc0ad); @@ -93,7 +64,6 @@ contract PufferModuleManagerIntegrationTest is IntegrationTestHelper { function _createRestakingOperator() internal returns (RestakingOperator) { RestakingOperator operator = moduleManager.createNewRestakingOperator({ metadataURI: "https://puffer.fi/metadata.json", - delegationApprover: address(0x5555555555555555555555555555555555555555), allocationDelay: 0 }); diff --git a/mainnet-contracts/test/unit/PufferModuleManager.t.sol b/mainnet-contracts/test/unit/PufferModuleManager.t.sol index a49b3d12..65ab1c77 100644 --- a/mainnet-contracts/test/unit/PufferModuleManager.t.sol +++ b/mainnet-contracts/test/unit/PufferModuleManager.t.sol @@ -168,38 +168,6 @@ contract PufferModuleManagerTest is UnitTestHelper { pufferModuleManager.callDeregisterOperatorFromAVS(operator, deregisterParams); } - function test_modifyOperatorDetails(address newDelegationApprover) public { - vm.assume(newDelegationApprover != address(0)); - - vm.startPrank(DAO); - RestakingOperator operator = _createRestakingOperator(); - - // Can't be called directly - vm.expectRevert(Unauthorized.selector); - operator.modifyOperatorDetails(newDelegationApprover); - - // Can be called through the PufferModuleManager - vm.expectEmit(true, true, true, true); - emit IPufferModuleManager.RestakingOperatorModified(address(operator), newDelegationApprover); - pufferModuleManager.callModifyOperatorDetails(operator, newDelegationApprover); - vm.stopPrank(); - } - - function test_updateOperatorMetadataURI(string memory newMetadataURI) public { - vm.startPrank(DAO); - RestakingOperator operator = _createRestakingOperator(); - - // Can't be called directly - vm.expectRevert(Unauthorized.selector); - operator.updateOperatorMetadataURI(newMetadataURI); - - vm.startPrank(DAO); - vm.expectEmit(true, true, true, true); - emit IPufferModuleManager.RestakingOperatorMetadataURIUpdated(address(operator), newMetadataURI); - pufferModuleManager.callUpdateMetadataURI(operator, newMetadataURI); - vm.stopPrank(); - } - function test_donation(bytes32 moduleName) public { address module = _createPufferModule(moduleName); (bool s,) = address(module).call{ value: 5 ether }(""); @@ -445,7 +413,6 @@ contract PufferModuleManagerTest is UnitTestHelper { function _createRestakingOperator() internal returns (RestakingOperator) { RestakingOperator operator = pufferModuleManager.createNewRestakingOperator({ metadataURI: "https://puffer.fi/metadata.json", - delegationApprover: address(0), allocationDelay: 500 }); From c3654ee3b20cb40dbf2bf909d6ced5cc8bdb6d37 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 23 Dec 2024 14:53:35 +0100 Subject: [PATCH 29/37] add queue & claim fork test --- mainnet-contracts/package.json | 4 +- .../DeployPufferModuleImplementation.s.sol | 20 +++++ .../script/DeployPufferModuleManager.s.sol | 15 ++++ .../script/DeployRestakingOperator.s.sol | 19 ++++ mainnet-contracts/script/DeployerHelper.s.sol | 34 +++++++ ...fferModuleManagerSlasher.integration.t.sol | 90 +++++++++++++++++++ 6 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 mainnet-contracts/test/fork-tests/PufferModuleManagerSlasher.integration.t.sol diff --git a/mainnet-contracts/package.json b/mainnet-contracts/package.json index 2ad96827..55fe8b5f 100644 --- a/mainnet-contracts/package.json +++ b/mainnet-contracts/package.json @@ -46,7 +46,7 @@ "lint": "yarn run lint:sol", "test:unit": "forge test --mp \"./test/unit/**/*.sol\" -vvv", "slither": "slither .", - "coverage": "forge coverage --force --no-match-coverage \"(script|test|mock|interface|node_modules|integrations|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\"", - "coverage-lcov": "forge coverage --force --no-match-coverage \"(script|test|mock|interface|node_modules|integrations|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\" --report lcov" + "coverage": "forge coverage --force --no-match-coverage \"(script|test|mock|interface|node_modules|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\"", + "coverage-lcov": "forge coverage --force --no-match-coverage \"(script|test|mock|interface|node_modules|echidna)\" --no-match-contract \"PufferModuleManagerHoleskyTestnetFFI\" --report lcov" } } diff --git a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol index 61b4fcc1..ab1a0ce9 100644 --- a/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol +++ b/mainnet-contracts/script/DeployPufferModuleImplementation.s.sol @@ -46,4 +46,24 @@ contract DeployPufferModuleImplementation is DeployerHelper { AccessManager(_getAccessManager()).execute(_getPufferModuleBeacon(), cd); } } + + function deployPufferModuleTests() public { + vm.startPrank(_getPaymaster()); + + PufferModule newImpl = new PufferModule({ + protocol: PufferProtocol(_getPufferProtocol()), + eigenPodManager: _getEigenPodManager(), + delegationManager: IDelegationManager(_getDelegationManager()), + moduleManager: PufferModuleManager(payable(_getPufferModuleManager())), + rewardsCoordinator: IRewardsCoordinator(_getRewardsCoordinator()) + }); + + vm.label(address(newImpl), "PufferModuleImplementation"); + + bytes memory cd = abi.encodeCall(UpgradeableBeacon.upgradeTo, address(newImpl)); + + if (block.chainid == holesky) { + AccessManager(_getAccessManager()).execute(_getPufferModuleBeacon(), cd); + } + } } diff --git a/mainnet-contracts/script/DeployPufferModuleManager.s.sol b/mainnet-contracts/script/DeployPufferModuleManager.s.sol index 7b9ea6eb..76e3136e 100644 --- a/mainnet-contracts/script/DeployPufferModuleManager.s.sol +++ b/mainnet-contracts/script/DeployPufferModuleManager.s.sol @@ -30,4 +30,19 @@ contract DeployPufferModuleManager is DeployerHelper { contractName: "PufferModuleManagerImplementation" }); } + + function deployPufferModuleManagerTests() public { + PufferModuleManager newPufferModuleManagerImplementation = new PufferModuleManager({ + pufferModuleBeacon: address(_getPufferModuleBeacon()), + restakingOperatorBeacon: address(_getRestakingOperatorBeacon()), + pufferProtocol: address(_getPufferProtocol()) + }); + + _consoleLogOrUpgradeUUPSPrank({ + proxyTarget: _getPufferModuleManager(), + implementation: address(newPufferModuleManagerImplementation), + data: "", + contractName: "PufferModuleManagerImplementation" + }); + } } diff --git a/mainnet-contracts/script/DeployRestakingOperator.s.sol b/mainnet-contracts/script/DeployRestakingOperator.s.sol index b53f44a3..57b84acb 100644 --- a/mainnet-contracts/script/DeployRestakingOperator.s.sol +++ b/mainnet-contracts/script/DeployRestakingOperator.s.sol @@ -42,4 +42,23 @@ contract DeployRestakingOperator is DeployerHelper { AccessManager(_getAccessManager()).execute(_getRestakingOperatorBeacon(), cd); } } + + function deployRestakingOperatorTests() public { + vm.startPrank(_getPaymaster()); + + RestakingOperator restakingOperatorImplementation = new RestakingOperator({ + delegationManager: IDelegationManager(_getEigenDelegationManager()), + allocationManager: IAllocationManager(_getEigenSlasher()), + moduleManager: PufferModuleManager(payable(_getPufferModuleManager())), + rewardsCoordinator: IRewardsCoordinator(_getRewardsCoordinator()) + }); + + vm.label(address(restakingOperatorImplementation), "RestakingOperatorImplementation"); + + bytes memory cd = abi.encodeCall(UpgradeableBeacon.upgradeTo, address(restakingOperatorImplementation)); + + if (block.chainid == holesky) { + AccessManager(_getAccessManager()).execute(_getRestakingOperatorBeacon(), cd); + } + } } diff --git a/mainnet-contracts/script/DeployerHelper.s.sol b/mainnet-contracts/script/DeployerHelper.s.sol index d8c847eb..d28d951b 100644 --- a/mainnet-contracts/script/DeployerHelper.s.sol +++ b/mainnet-contracts/script/DeployerHelper.s.sol @@ -56,6 +56,40 @@ abstract contract DeployerHelper is Script { } } + /** + * @dev Used only for testnet deployment and fork tests, where the _paymaster is the deployer + */ + function _consoleLogOrUpgradeUUPSPrank( + address proxyTarget, + address implementation, + bytes memory data, + string memory contractName + ) internal { + vm.startPrank(_getPaymaster()); + vm.label(implementation, contractName); + console.log("Deployed", contractName, "at", implementation); + + if (block.chainid == holesky) { + AccessManager(_getAccessManager()).execute( + proxyTarget, abi.encodeCall(UUPSUpgradeable.upgradeToAndCall, (address(implementation), data)) + ); + } else { + bytes memory upgradeCallData = + abi.encodeCall(UUPSUpgradeable.upgradeToAndCall, (address(implementation), data)); + console.log("Queue TX From Timelock to -> ", proxyTarget); + console.logBytes(upgradeCallData); + console.log("================================================"); + } + } + + function _getBeaconChainStrategy() internal view returns (address) { + if (block.chainid == holesky) { + return 0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0; + } + + revert("BEACON_CHAIN_STRATEGY not available for this chain"); + } + function _getTreasury() internal view returns (address) { if (block.chainid == mainnet) { // https://etherscan.io/address/0x946Ae7b21de3B0793Bb469e263517481B74A6950 diff --git a/mainnet-contracts/test/fork-tests/PufferModuleManagerSlasher.integration.t.sol b/mainnet-contracts/test/fork-tests/PufferModuleManagerSlasher.integration.t.sol new file mode 100644 index 00000000..6d076bd8 --- /dev/null +++ b/mainnet-contracts/test/fork-tests/PufferModuleManagerSlasher.integration.t.sol @@ -0,0 +1,90 @@ +pragma solidity >=0.8.0 <0.9.0; + +import { Test } from "forge-std/Test.sol"; +import { DeployerHelper } from "../../script/DeployerHelper.s.sol"; +import { DeployEverything } from "script/DeployEverything.s.sol"; +import { DeployEverything } from "script/DeployEverything.s.sol"; +import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; +import { IStrategy } from "../../src/interface/Eigenlayer-Slashing/IStrategy.sol"; +import { IDelegationManagerTypes } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; +import { DeployRestakingOperator } from "../../script/DeployRestakingOperator.s.sol"; +import { DeployPufferModuleImplementation } from "../../script/DeployPufferModuleImplementation.s.sol"; +import { IDelegationManager } from "../../src/interface/Eigenlayer-Slashing/IDelegationManager.sol"; +import { DeployPufferModuleManager } from "../../script/DeployPufferModuleManager.s.sol"; + +contract PufferModuleManagerSlasherIntegrationTest is Test, DeployerHelper { + PufferModuleManager public pufferModuleManager; + address PUFFER_MODULE_0_HOLESKY = 0x9017a172578458E1204691D6E1dB92ca61381655; + address EIGENPOD_0_HOLESKY = 0xeD9B08B8958B89E7A9008CAc0937E46F73Bf8f52; + address RESTAKING_OPERATOR_0_HOLESKY = 0x57b6FdEF3A23B81547df68F44e5524b987755c99; + bytes32 PUFFER_MODULE_0_NAME = bytes32("PUFFER_MODULE_0"); + + DeployPufferModuleManager deployPufferModuleManager; + DeployPufferModuleImplementation deployPufferModule; + DeployRestakingOperator deployRestakingOperator; + + uint32 START_BLOCK = 2994229; // Dec-23-2024 09:43:00 AM +UTC + + function setUp() public { + vm.createSelectFork(vm.rpcUrl("holesky"), START_BLOCK); + + // I want to use the deployment scripts to deploy the contracts in tests. + deployPufferModuleManager = new DeployPufferModuleManager(); + deployPufferModule = new DeployPufferModuleImplementation(); + deployRestakingOperator = new DeployRestakingOperator(); + + // To do that, we must allow cheatcodes for those scripts + vm.allowCheatcodes(address(deployPufferModuleManager)); + vm.allowCheatcodes(address(deployPufferModule)); + vm.allowCheatcodes(address(deployRestakingOperator)); + + deployPufferModuleManager.deployPufferModuleManagerTests(); + deployPufferModule.deployPufferModuleTests(); + deployRestakingOperator.deployRestakingOperatorTests(); + + pufferModuleManager = PufferModuleManager(payable(_getPufferModuleManager())); + } + + // Queue new withdrawals + function test_new_queue_withdrawals() public { + vm.startPrank(_getPaymaster()); + pufferModuleManager.callQueueWithdrawals(PUFFER_MODULE_0_NAME, 0.1 ether); + } + + // New withdrawal flow + function test_queue_and_claim_withdrawals() public { + vm.startPrank(_getPaymaster()); + + uint256 amount = 0.1 ether; + pufferModuleManager.callQueueWithdrawals(PUFFER_MODULE_0_NAME, amount); + + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = IStrategy(_getBeaconChainStrategy()); + + uint256[] memory scaledShares = new uint256[](1); + scaledShares[0] = amount; + + IDelegationManagerTypes.Withdrawal[] memory withdrawals = new IDelegationManagerTypes.Withdrawal[](1); + withdrawals[0] = IDelegationManagerTypes.Withdrawal({ + staker: PUFFER_MODULE_0_HOLESKY, + delegatedTo: RESTAKING_OPERATOR_0_HOLESKY, + withdrawer: PUFFER_MODULE_0_HOLESKY, + nonce: 42, + startBlock: START_BLOCK, + strategies: strategies, + scaledShares: scaledShares + }); + + IERC20[][] memory tokens = new IERC20[][](1); + tokens[0] = new IERC20[](1); + tokens[0][0] = IERC20(_getBeaconChainStrategy()); + bool[] memory receiveAsTokens = new bool[](1); + receiveAsTokens[0] = true; + + vm.roll(START_BLOCK + 50 + 1); // on Holesky its 50 blocks wait time, in Production it will be 14 days in blocks.. + + pufferModuleManager.callCompleteQueuedWithdrawals(PUFFER_MODULE_0_NAME, withdrawals, tokens, receiveAsTokens); + } +} From a32646e1aaafe0a30289a62ca1b07724d99b600c Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 23 Dec 2024 15:33:34 +0100 Subject: [PATCH 30/37] Slashing calldata access control --- .../07_GenerateSlashingELCalldata.s.sol | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol index d2fa8592..8e0cd9bd 100644 --- a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol +++ b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol @@ -4,20 +4,36 @@ pragma solidity >=0.8.0 <0.9.0; import { Script } from "forge-std/Script.sol"; import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol"; -import { ROLE_ID_DAO } from "../../script/Roles.sol"; +import { ROLE_ID_DAO, ROLE_ID_OPERATIONS_PAYMASTER } from "../../script/Roles.sol"; import { PufferModuleManager } from "../../src/PufferModuleManager.sol"; +// forge script script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol:GenerateSlashingELCalldata -vvvv --sig "run(address)(bytes memory)" PUFFER_MODULE_MANAGER_PROXY_ADDRESS contract GenerateSlashingELCalldata is Script { function run(address pufferModuleManagerProxy) public pure returns (bytes memory) { - bytes[] memory calldatas = new bytes[](1); + bytes[] memory calldatas = new bytes[](2); - bytes4[] memory daoSelectors = new bytes4[](2); - daoSelectors[0] = PufferModuleManager.callRegisterOperatorToAVS.selector; - daoSelectors[1] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; + bytes4[] memory daoSelectors = new bytes4[](9); + daoSelectors[0] = PufferModuleManager.callSetClaimerFor.selector; + daoSelectors[1] = PufferModuleManager.callSetProofSubmitter.selector; + daoSelectors[2] = PufferModuleManager.createNewRestakingOperator.selector; + daoSelectors[3] = PufferModuleManager.callDelegateTo.selector; + daoSelectors[4] = PufferModuleManager.callUndelegate.selector; + daoSelectors[5] = PufferModuleManager.callRegisterOperatorToAVS.selector; + daoSelectors[6] = PufferModuleManager.customExternalCall.selector; + daoSelectors[7] = PufferModuleManager.callDeregisterOperatorFromAVS.selector; + daoSelectors[8] = PufferModuleManager.updateAVSRegistrationSignatureProof.selector; calldatas[0] = abi.encodeCall(AccessManager.setTargetFunctionRole, (pufferModuleManagerProxy, daoSelectors, ROLE_ID_DAO)); + bytes4[] memory paymasterSelectors = new bytes4[](3); + paymasterSelectors[0] = PufferModuleManager.callCompleteQueuedWithdrawals.selector; + paymasterSelectors[1] = PufferModuleManager.transferRewardsToTheVault.selector; + paymasterSelectors[2] = PufferModuleManager.callQueueWithdrawals.selector; + + calldatas[1] = + abi.encodeCall(AccessManager.setTargetFunctionRole, (pufferModuleManagerProxy, paymasterSelectors, ROLE_ID_OPERATIONS_PAYMASTER)); + bytes memory encodedMulticall = abi.encodeCall(Multicall.multicall, (calldatas)); return encodedMulticall; From c357872b5e04fcfeed22da7cd3b6062e592fd95c Mon Sep 17 00:00:00 2001 From: bxmmm1 Date: Mon, 23 Dec 2024 17:13:45 +0000 Subject: [PATCH 31/37] forge fmt --- .../07_GenerateSlashingELCalldata.s.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol index 8e0cd9bd..fd974357 100644 --- a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol +++ b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol @@ -31,8 +31,10 @@ contract GenerateSlashingELCalldata is Script { paymasterSelectors[1] = PufferModuleManager.transferRewardsToTheVault.selector; paymasterSelectors[2] = PufferModuleManager.callQueueWithdrawals.selector; - calldatas[1] = - abi.encodeCall(AccessManager.setTargetFunctionRole, (pufferModuleManagerProxy, paymasterSelectors, ROLE_ID_OPERATIONS_PAYMASTER)); + calldatas[1] = abi.encodeCall( + AccessManager.setTargetFunctionRole, + (pufferModuleManagerProxy, paymasterSelectors, ROLE_ID_OPERATIONS_PAYMASTER) + ); bytes memory encodedMulticall = abi.encodeCall(Multicall.multicall, (calldatas)); From 6af4bb4d9ce5a364ce5913081cca58ca5ca51ee1 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 3 Jan 2025 17:30:24 +0100 Subject: [PATCH 32/37] increase test coverage --- .../07_GenerateSlashingELCalldata.s.sol | 6 +- .../test/mocks/MockAeraVault.sol | 6 +- .../unit/PufferRevenueDepositorTest.t.sol | 22 ++++-- mainnet-contracts/test/unit/PufferVault.t.sol | 67 +++++++++++++++++++ 4 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 mainnet-contracts/test/unit/PufferVault.t.sol diff --git a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol index 8e0cd9bd..fd974357 100644 --- a/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol +++ b/mainnet-contracts/script/AccessManagerMigrations/07_GenerateSlashingELCalldata.s.sol @@ -31,8 +31,10 @@ contract GenerateSlashingELCalldata is Script { paymasterSelectors[1] = PufferModuleManager.transferRewardsToTheVault.selector; paymasterSelectors[2] = PufferModuleManager.callQueueWithdrawals.selector; - calldatas[1] = - abi.encodeCall(AccessManager.setTargetFunctionRole, (pufferModuleManagerProxy, paymasterSelectors, ROLE_ID_OPERATIONS_PAYMASTER)); + calldatas[1] = abi.encodeCall( + AccessManager.setTargetFunctionRole, + (pufferModuleManagerProxy, paymasterSelectors, ROLE_ID_OPERATIONS_PAYMASTER) + ); bytes memory encodedMulticall = abi.encodeCall(Multicall.multicall, (calldatas)); diff --git a/mainnet-contracts/test/mocks/MockAeraVault.sol b/mainnet-contracts/test/mocks/MockAeraVault.sol index efa74d51..2389e2a3 100644 --- a/mainnet-contracts/test/mocks/MockAeraVault.sol +++ b/mainnet-contracts/test/mocks/MockAeraVault.sol @@ -6,7 +6,11 @@ import { IAeraVault, AssetValue } from "src/interface/Other/IAeraVault.sol"; contract MockAeraVault is IAeraVault { function deposit(AssetValue[] memory amounts) external { } - function withdraw(AssetValue[] memory amounts) external { } + function withdraw(AssetValue[] memory amounts) external { + for (uint256 i = 0; i < amounts.length; ++i) { + amounts[i].asset.transfer(msg.sender, amounts[i].value); + } + } function setGuardianAndFeeRecipient(address, address) external { } diff --git a/mainnet-contracts/test/unit/PufferRevenueDepositorTest.t.sol b/mainnet-contracts/test/unit/PufferRevenueDepositorTest.t.sol index 42ec7d39..f989cb13 100644 --- a/mainnet-contracts/test/unit/PufferRevenueDepositorTest.t.sol +++ b/mainnet-contracts/test/unit/PufferRevenueDepositorTest.t.sol @@ -6,6 +6,8 @@ import { IPufferRevenueDepositor } from "src/interface/IPufferRevenueDepositor.s import { IWETH } from "src/interface/IWETH.sol"; import { ROLE_ID_REVENUE_DEPOSITOR } from "../../script/Roles.sol"; import { PufferRevenueDepositor } from "src/PufferRevenueDepositor.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IAeraVault, AssetValue } from "src/interface/Other/IAeraVault.sol"; contract AeraVaultMock { IWETH public immutable WETH; @@ -31,12 +33,15 @@ contract AeraVaultMock { * forge test --mc PufferRevenueDepositorTest -vvvv */ contract PufferRevenueDepositorTest is UnitTestHelper { - AeraVaultMock public aeraVault; + IAeraVault public aeraVault; + AeraVaultMock public aeraVaultMock; function setUp() public override { super.setUp(); - aeraVault = new AeraVaultMock(address(weth), address(revenueDepositor)); + aeraVault = IAeraVault(revenueDepositor.AERA_VAULT()); + // Deposit 1000 WETH to the AeraVault + aeraVaultMock = new AeraVaultMock(address(weth), address(revenueDepositor)); // Deposit 1000 WETH to the AeraVault deal(address(weth), address(aeraVault), 1000 ether); @@ -46,9 +51,7 @@ contract PufferRevenueDepositorTest is UnitTestHelper { } function test_setup() public view { - assertEq(address(aeraVault.WETH()), address(weth), "WETH should be the same"); assertEq(weth.balanceOf(address(aeraVault)), 1000 ether, "AeraVault should have 1000 WETH"); - assertEq(aeraVault.REVENUE_DEPOSITOR(), address(revenueDepositor), "Revenue depositor should be the same"); } /** @@ -66,6 +69,12 @@ contract PufferRevenueDepositorTest is UnitTestHelper { assertTrue(address(revenueDepositor.PUFFER_VAULT()) != address(0), "PufferVault should not be 0"); } + function test_withdrawAndDeposit() public withRewardsDistributionWindow(1 days) { + vm.deal(address(aeraVault), 10 ether); + vm.startPrank(OPERATIONS_MULTISIG); + revenueDepositor.withdrawAndDeposit(); + } + function test_setRewardsDistributionWindow() public { assertEq(revenueDepositor.getRewardsDistributionWindow(), 0, "Rewards distribution window should be 0"); @@ -196,6 +205,7 @@ contract PufferRevenueDepositorTest is UnitTestHelper { // Withdraw 100 WETH from AeraVault and deposit it into PufferVault in 1 tx function test_callTargets() public { + deal(address(weth), address(aeraVault), 100 ether); vm.startPrank(OPERATIONS_MULTISIG); address[] memory targets = new address[](2); @@ -203,7 +213,9 @@ contract PufferRevenueDepositorTest is UnitTestHelper { targets[1] = address(revenueDepositor); bytes[] memory data = new bytes[](2); - data[0] = abi.encodeCall(aeraVault.withdraw, (100 ether)); + AssetValue[] memory amounts = new AssetValue[](1); + amounts[0] = AssetValue({ asset: IERC20(address(weth)), value: 100 ether }); + data[0] = abi.encodeCall(IAeraVault.withdraw, (amounts)); data[1] = abi.encodeCall(revenueDepositor.depositRevenue, ()); vm.expectEmit(true, true, true, true); diff --git a/mainnet-contracts/test/unit/PufferVault.t.sol b/mainnet-contracts/test/unit/PufferVault.t.sol new file mode 100644 index 00000000..be4914a1 --- /dev/null +++ b/mainnet-contracts/test/unit/PufferVault.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import { UnitTestHelper } from "../helpers/UnitTestHelper.sol"; +import { IPufferVaultV2 } from "src/interface/IPufferVaultV2.sol"; + +contract PufferVaultTest is UnitTestHelper { + uint256 pointZeroZeroOne = 0.0001e18; + + function test_setup_vault() public view { + assertEq(pufferVault.asset(), address(weth), "asset should be WETH"); + assertEq(pufferVault.totalSupply(), 1000 ether, "totalSupply should be 1000 ETH"); + assertEq(pufferVault.totalAssets(), 1000 ether, "totalAssets should be 1000 ETH"); + assertEq(pufferVault.getExitFeeBasisPoints(), 100, "fee should be 100"); + assertEq(pufferVault.decimals(), 18, "decimals should be 18"); + } + + modifier withZeroExitFeeBasisPoints() { + vm.startPrank(address(timelock)); + pufferVault.setExitFeeBasisPoints(0); + vm.stopPrank(); + _; + } + + function test_setExitFeeBasisPoints() public withZeroExitFeeBasisPoints { + vm.startPrank(address(timelock)); + pufferVault.setExitFeeBasisPoints(100); + vm.stopPrank(); + } + + function test_setExitFeeBasisPoints_invalid_value() public { + vm.startPrank(address(timelock)); + vm.expectRevert(IPufferVaultV2.InvalidExitFeeBasisPoints.selector); + pufferVault.setExitFeeBasisPoints(10000); + vm.stopPrank(); + } + + function test_previewRedeem() public withZeroExitFeeBasisPoints { + uint256 redeemAmount = pufferVault.previewRedeem(1 ether); + assertApproxEqRel(redeemAmount, 1 ether, pointZeroZeroOne, "redeemAmount should be 1 ether"); + } + + function test_previewWithdraw() public withZeroExitFeeBasisPoints { + uint256 withdrawAmount = pufferVault.previewWithdraw(1 ether); + assertApproxEqRel(withdrawAmount, 1 ether, pointZeroZeroOne, "withdrawAmount should be 1 ether"); + } + + function test_redeem() public withZeroExitFeeBasisPoints { + vm.deal(alice, 1 ether); + + vm.startPrank(alice); + pufferVault.approve(address(this), 1 ether); + pufferVault.depositETH{ value: 1 ether }(alice); + + pufferVault.redeem(1 ether, alice, alice); + vm.stopPrank(); + } + + function test_mint() public withZeroExitFeeBasisPoints { + vm.deal(alice, 1 ether); + + vm.startPrank(alice); + weth.approve(address(pufferVault), 1 ether); + + pufferVault.mint(1 ether, alice); + } +} From cc3bd091dc6642183ff73a488ab8d47841953b1d Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 3 Jan 2025 17:38:20 +0100 Subject: [PATCH 33/37] fix failing test --- mainnet-contracts/script/GenerateAccessManagerCallData.sol | 6 ++++-- mainnet-contracts/test/mocks/WETH9.sol | 2 +- mainnet-contracts/test/unit/PufferVault.t.sol | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/mainnet-contracts/script/GenerateAccessManagerCallData.sol b/mainnet-contracts/script/GenerateAccessManagerCallData.sol index 3f5770e2..571f76d2 100644 --- a/mainnet-contracts/script/GenerateAccessManagerCallData.sol +++ b/mainnet-contracts/script/GenerateAccessManagerCallData.sol @@ -7,6 +7,7 @@ import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol"; import { console } from "forge-std/console.sol"; import { PufferVaultV5 } from "../src/PufferVaultV5.sol"; import { PufferDepositorV2 } from "../src/PufferDepositorV2.sol"; +import { ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import { PufferDepositor } from "../src/PufferDepositor.sol"; import { PUBLIC_ROLE, ROLE_ID_PUFFER_PROTOCOL, ROLE_ID_OPERATIONS_MULTISIG } from "./Roles.sol"; @@ -39,12 +40,13 @@ contract GenerateAccessManagerCallData is Script { function _getPublicSelectorsCalldata(address pufferVaultProxy) internal pure returns (bytes memory) { // Public selectors for PufferVault - bytes4[] memory publicSelectors = new bytes4[](4); + bytes4[] memory publicSelectors = new bytes4[](6); publicSelectors[0] = PufferVaultV5.withdraw.selector; publicSelectors[1] = PufferVaultV5.redeem.selector; publicSelectors[2] = PufferVaultV5.depositETH.selector; publicSelectors[3] = PufferVaultV5.depositStETH.selector; - // `deposit` and `mint` are already `restricted` and allowed for PUBLIC_ROLE (PufferVault deployment) + publicSelectors[4] = ERC4626.deposit.selector; + publicSelectors[5] = ERC4626.mint.selector; return abi.encodeWithSelector( AccessManager.setTargetFunctionRole.selector, pufferVaultProxy, publicSelectors, PUBLIC_ROLE diff --git a/mainnet-contracts/test/mocks/WETH9.sol b/mainnet-contracts/test/mocks/WETH9.sol index 9bf41413..4e878da3 100644 --- a/mainnet-contracts/test/mocks/WETH9.sol +++ b/mainnet-contracts/test/mocks/WETH9.sol @@ -56,7 +56,7 @@ contract WETH9 is IWETH { } function transferFrom(address src, address dst, uint256 wad) public returns (bool) { - require(balanceOf[src] >= wad, "?????"); + require(balanceOf[src] >= wad, "sender does not have enough balance"); if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { require(allowance[src][msg.sender] >= wad); diff --git a/mainnet-contracts/test/unit/PufferVault.t.sol b/mainnet-contracts/test/unit/PufferVault.t.sol index be4914a1..02106ebe 100644 --- a/mainnet-contracts/test/unit/PufferVault.t.sol +++ b/mainnet-contracts/test/unit/PufferVault.t.sol @@ -56,8 +56,8 @@ contract PufferVaultTest is UnitTestHelper { vm.stopPrank(); } - function test_mint() public withZeroExitFeeBasisPoints { - vm.deal(alice, 1 ether); + function test_mint_vault_v5() public withZeroExitFeeBasisPoints { + deal(address(weth), alice, 1 ether); vm.startPrank(alice); weth.approve(address(pufferVault), 1 ether); From f0d66b9a289ad3f163e57f3809bd5722a5939cad Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Jan 2025 09:00:22 +0100 Subject: [PATCH 34/37] move variable --- mainnet-contracts/src/RestakingOperator.sol | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mainnet-contracts/src/RestakingOperator.sol b/mainnet-contracts/src/RestakingOperator.sol index 668f6102..a94648b7 100644 --- a/mainnet-contracts/src/RestakingOperator.sol +++ b/mainnet-contracts/src/RestakingOperator.sol @@ -21,14 +21,8 @@ import { IRewardsCoordinator } from "./interface/Eigenlayer-Slashing/IRewardsCoo */ contract RestakingOperator is IERC1271, Initializable, AccessManagedUpgradeable { using Address for address; - // keccak256(abi.encode(uint256(keccak256("RestakingOperator.storage")) - 1)) & ~bytes32(uint256(0xff)) - // slither-disable-next-line unused-state - - /** - * @dev Upgradeable contract from EigenLayer - */ - IRewardsCoordinator public immutable EIGEN_REWARDS_COORDINATOR; + // keccak256(abi.encode(uint256(keccak256("RestakingOperator.storage")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant _RESTAKING_OPERATOR_STORAGE = 0x2182a68f8e463a6b4c76f5de5bb25b7b51ccc88cb3b9ba6c251c356b50555100; @@ -49,6 +43,11 @@ contract RestakingOperator is IERC1271, Initializable, AccessManagedUpgradeable mapping(bytes32 digestHash => address signer) hashSigners; } + /** + * @dev Upgradeable contract from EigenLayer + */ + IRewardsCoordinator public immutable EIGEN_REWARDS_COORDINATOR; + /** * @dev Upgradeable contract from EigenLayer */ From 1b147b91c461c24b37da011074cf16128e8556ae Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Jan 2025 10:13:30 +0100 Subject: [PATCH 35/37] add vault v5 fork tests to make sure that the interface stayed the same --- .../test/fork-tests/PufferVaultForkTest.t.sol | 63 +++++++++++++++++++ mainnet-contracts/test/unit/PufferVault.t.sol | 6 ++ 2 files changed, 69 insertions(+) create mode 100644 mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol diff --git a/mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol b/mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol new file mode 100644 index 00000000..ece4192f --- /dev/null +++ b/mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; +import { IPufferVault } from "../../src/interface/IPufferVault.sol"; +import { IPufferVaultV2 } from "../../src/interface/IPufferVaultV2.sol"; + +contract PufferVaultForkTest is MainnetForkTestHelper { + function setUp() public virtual override { } + + // In this test, we initiate ETH withdrawal from Lido + function test_initiateETHWithdrawalsFromLido() public { + vm.createSelectFork(vm.rpcUrl("mainnet"), 21549844); // Jan-04-2025 08:13:23 AM +UTC + _setupLiveContracts(); + + vm.startPrank(_getOPSMultisig()); + + uint256[] memory amounts = new uint256[](1); + amounts[0] = 10 ether; + + uint256[] memory requestIds = pufferVault.initiateETHWithdrawalsFromLido(amounts); + requestIds[0] = 66473; // That is the next request id for this test + + vm.expectEmit(true, true, true, true); + emit IPufferVault.RequestedWithdrawals(requestIds); + pufferVault.initiateETHWithdrawalsFromLido(amounts); + } + + // In this test, we claim some queued withdrawal from Lido + function test_claimETHWithdrawalsFromLido() public { + // Different fork + vm.createSelectFork(vm.rpcUrl("mainnet"), 21378494); // Dec-11-2024 09:52:59 AM +UTC + _setupLiveContracts(); + + vm.startPrank(_getOPSMultisig()); + + uint256[] memory requestIds = new uint256[](1); + requestIds[0] = 62744; // That is the next request id for this test + + uint256 balanceBefore = address(pufferVault).balance; + + vm.expectEmit(true, true, true, true); + emit IPufferVault.ClaimedWithdrawals(requestIds); + pufferVault.claimWithdrawalsFromLido(requestIds); + + uint256 balanceAfter = address(pufferVault).balance; + assertEq(balanceAfter, balanceBefore + 107.293916980728143835 ether, "Balance should increase by ~107 ether"); + } + + // Prevent deposit and withdraw in the same transaction + function test_depositAndWithdrawRevertsInTheSameTx() public { + vm.createSelectFork(vm.rpcUrl("mainnet"), 21378494); // Dec-11-2024 09:52:59 AM +UTC + _setupLiveContracts(); + + vm.deal(alice, 1 ether); + vm.startPrank(alice); + + pufferVault.depositETH{ value: 1 ether }(alice); + + vm.expectRevert(IPufferVaultV2.DepositAndWithdrawalForbidden.selector); + pufferVault.redeem(1 ether, alice, alice); + } +} diff --git a/mainnet-contracts/test/unit/PufferVault.t.sol b/mainnet-contracts/test/unit/PufferVault.t.sol index 02106ebe..4951e70b 100644 --- a/mainnet-contracts/test/unit/PufferVault.t.sol +++ b/mainnet-contracts/test/unit/PufferVault.t.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.0 <0.9.0; import { UnitTestHelper } from "../helpers/UnitTestHelper.sol"; import { IPufferVaultV2 } from "src/interface/IPufferVaultV2.sol"; +import { ERC4626Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; contract PufferVaultTest is UnitTestHelper { uint256 pointZeroZeroOne = 0.0001e18; @@ -52,6 +53,11 @@ contract PufferVaultTest is UnitTestHelper { pufferVault.approve(address(this), 1 ether); pufferVault.depositETH{ value: 1 ether }(alice); + vm.expectRevert( + abi.encodeWithSelector(ERC4626Upgradeable.ERC4626ExceededMaxRedeem.selector, alice, 100 ether, 1 ether) + ); + pufferVault.redeem(100 ether, alice, alice); + pufferVault.redeem(1 ether, alice, alice); vm.stopPrank(); } From b340a38c4c6bb3b6fcab1d292bc2026a24a16970 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Jan 2025 11:29:02 +0100 Subject: [PATCH 36/37] increase coverage --- mainnet-contracts/script/DeployPufETH.s.sol | 2 +- mainnet-contracts/script/UpgradePufETH.s.sol | 2 ++ .../test/fork-tests/PufferVaultForkTest.t.sol | 3 +++ .../test/mocks/LidoWithdrawalQueueMock.sol | 16 ++++++++++++---- mainnet-contracts/test/unit/PufferVault.t.sol | 12 ++++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/mainnet-contracts/script/DeployPufETH.s.sol b/mainnet-contracts/script/DeployPufETH.s.sol index e92ecd19..9f503165 100644 --- a/mainnet-contracts/script/DeployPufETH.s.sol +++ b/mainnet-contracts/script/DeployPufETH.s.sol @@ -109,7 +109,7 @@ contract DeployPufETH is BaseScript { // Deploy implementation contracts pufferVaultImplementation = new PufferVault(IStETH(stETHAddress), lidoWithdrawalQueue); - vm.label(address(pufferVaultImplementation), "PufferVaultImplementation"); + vm.label(address(pufferVaultImplementation), "PufferVaultOriginalImplementation"); pufferDepositorImplementation = new PufferDepositor({ stETH: IStETH(stETHAddress), pufferVault: PufferVault(payable(vaultProxy)) }); vm.label(address(pufferDepositorImplementation), "PufferDepositorImplementation"); diff --git a/mainnet-contracts/script/UpgradePufETH.s.sol b/mainnet-contracts/script/UpgradePufETH.s.sol index 972523fd..79daef5f 100644 --- a/mainnet-contracts/script/UpgradePufETH.s.sol +++ b/mainnet-contracts/script/UpgradePufETH.s.sol @@ -75,6 +75,8 @@ contract UpgradePufETH is BaseScript { IPufferRevenueDepositor(revenueDepositor) ); + vm.label(address(newImplementation), "PufferVaultV5Implementation"); + UUPSUpgradeable(deployment.pufferVault).upgradeToAndCall(address(newImplementation), ""); } } diff --git a/mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol b/mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol index ece4192f..8a8192ab 100644 --- a/mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol +++ b/mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol @@ -5,6 +5,9 @@ import { MainnetForkTestHelper } from "../MainnetForkTestHelper.sol"; import { IPufferVault } from "../../src/interface/IPufferVault.sol"; import { IPufferVaultV2 } from "../../src/interface/IPufferVaultV2.sol"; +/** + * @notice For some reason the code coverage doesn't consider that this mainnet fork tests increase the code coverage.. + */ contract PufferVaultForkTest is MainnetForkTestHelper { function setUp() public virtual override { } diff --git a/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol b/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol index 89a27b19..6f413c0b 100644 --- a/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol +++ b/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol @@ -2,12 +2,20 @@ pragma solidity >=0.8.0 <0.9.0; import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQueue.sol"; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -contract LidoWithdrawalQueueMock is ILidoWithdrawalQueue { - function requestWithdrawals(uint256[] calldata _amounts, address _owner) +contract LidoWithdrawalQueueMock is ILidoWithdrawalQueue, ERC721 { + + constructor() ERC721("LidoWithdrawalQueueMock", "LIDO") {} + function requestWithdrawals(uint256[] calldata, address) external - returns (uint256[] memory requestIds) - { } + returns (uint256[] memory) + { + _mint(msg.sender, 1); + uint256[] memory requestIds = new uint256[](1); + requestIds[0] = 1; + return requestIds; + } function claimWithdrawal(uint256 _requestId) external { } } diff --git a/mainnet-contracts/test/unit/PufferVault.t.sol b/mainnet-contracts/test/unit/PufferVault.t.sol index 4951e70b..d524540b 100644 --- a/mainnet-contracts/test/unit/PufferVault.t.sol +++ b/mainnet-contracts/test/unit/PufferVault.t.sol @@ -70,4 +70,16 @@ contract PufferVaultTest is UnitTestHelper { pufferVault.mint(1 ether, alice); } + + // See mainnet-contracts/test/fork-tests/PufferVaultForkTest.t.sol for real test + // For some reason code coverage doesn't account for tests in the file above + function test_initiateETHWithdrawalsFromLidoDummy() public { + vm.startPrank(OPERATIONS_MULTISIG); + + pufferVault.initiateETHWithdrawalsFromLido(new uint256[](1)); + + uint256[] memory requestIds = new uint256[](1); + requestIds[0] = 1; + pufferVault.claimWithdrawalsFromLido(requestIds); + } } From dfd038c7982f3b07a7123b47154406796445dd1c Mon Sep 17 00:00:00 2001 From: bxmmm1 Date: Mon, 6 Jan 2025 10:29:38 +0000 Subject: [PATCH 37/37] forge fmt --- mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol b/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol index 6f413c0b..0e933379 100644 --- a/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol +++ b/mainnet-contracts/test/mocks/LidoWithdrawalQueueMock.sol @@ -5,12 +5,9 @@ import { ILidoWithdrawalQueue } from "../../src/interface/Lido/ILidoWithdrawalQu import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract LidoWithdrawalQueueMock is ILidoWithdrawalQueue, ERC721 { + constructor() ERC721("LidoWithdrawalQueueMock", "LIDO") { } - constructor() ERC721("LidoWithdrawalQueueMock", "LIDO") {} - function requestWithdrawals(uint256[] calldata, address) - external - returns (uint256[] memory) - { + function requestWithdrawals(uint256[] calldata, address) external returns (uint256[] memory) { _mint(msg.sender, 1); uint256[] memory requestIds = new uint256[](1); requestIds[0] = 1;