diff --git a/src/eigenlayer/ObolEigenLayerPodController.sol b/src/eigenlayer/ObolEigenLayerPodController.sol deleted file mode 100644 index f47dd57..0000000 --- a/src/eigenlayer/ObolEigenLayerPodController.sol +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.19; - -import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; -import {ERC20} from "solmate/tokens/ERC20.sol"; -import {Ownable} from "solady/auth/Ownable.sol"; -import {IEigenLayerUtils, IEigenPodManager, IDelayedWithdrawalRouter} from "../interfaces/IEigenLayer.sol"; - -/// @title ObolEigenLayerPodController -/// @author Obol Labs -/// @notice A contract for controlling an Eigenpod and withdrawing the balance into an Obol Split -/// @dev The address returned should be used as the EigenPodController address -contract ObolEigenLayerPodController { - /// @dev returned on failed call - error CallFailed(bytes data); - /// @dev If Invalid fee setup - error Invalid_FeeSetup(); - /// @dev Invalid fee share - error Invalid_FeeShare(); - /// @dev user unauthorized - error Unauthorized(); - /// @dev contract already initialized - error AlreadyInitialized(); - - /// @dev Emiited on intialize - event Initialized(address eigenPod, address owner); - - /// ----------------------------------------------------------------------- - /// libraries - /// ----------------------------------------------------------------------- - using SafeTransferLib for address; - using SafeTransferLib for ERC20; - - uint256 internal constant PERCENTAGE_SCALE = 1e5; - - /// ----------------------------------------------------------------------- - /// storage - immutables - /// ----------------------------------------------------------------------- - - /// @notice address of Eigenlayer delegation manager - /// @dev This is the address of the delegation manager transparent proxy - address public immutable eigenLayerDelegationManager; - - /// @notice address of EigenLayerPod Manager - /// @dev this is the pod manager transparent proxy - IEigenPodManager public immutable eigenLayerPodManager; - - /// @notice address of delay withdrawal router - IDelayedWithdrawalRouter public immutable delayedWithdrawalRouter; - - /// @notice fee address - address public immutable feeRecipient; - - /// @notice fee share. Represented as an integer from 1->10000 (100%) - uint256 public immutable feeShare; - - /// ----------------------------------------------------------------------- - /// storage - /// ----------------------------------------------------------------------- - - /// @notice address of deployed Eigen pod - address public eigenPod; - - /// @notice address of a withdrawalAddress - address public withdrawalAddress; - - /// @notice address of owner - address public owner; - - modifier onlyOwner() { - if (msg.sender != owner) revert Unauthorized(); - _; - } - - constructor( - address recipient, - uint256 share, - address delegationManager, - address eigenPodManager, - address withdrawalRouter - ) { - if (recipient != address(0) && share == 0) revert Invalid_FeeSetup(); - if (share > PERCENTAGE_SCALE) revert Invalid_FeeShare(); - - feeRecipient = recipient; - feeShare = share; - eigenLayerDelegationManager = delegationManager; - eigenLayerPodManager = IEigenPodManager(eigenPodManager); - delayedWithdrawalRouter = IDelayedWithdrawalRouter(withdrawalRouter); - } - - /// @dev Enables contract to receive ETH - // defined on the clone implementation - // receive() external payable {} - - /// @notice initializes the controller - /// @param _owner address of the controller owner - /// @param _withdrawalAddress address to receive funds - function initialize(address _owner, address _withdrawalAddress) external { - if (owner != address(0)) revert AlreadyInitialized(); - - eigenPod = eigenLayerPodManager.createPod(); - owner = _owner; - withdrawalAddress = _withdrawalAddress; - - emit Initialized(eigenPod, _owner); - } - - /// @notice Call the eigenPod contract - /// @param data to call eigenPod contract - function callEigenPod(bytes calldata data) external payable onlyOwner { - _executeCall(address(eigenPod), msg.value, data); - } - - /// @notice Call the Eigenlayer delegation Manager contract - /// @param data to call eigenPod contract - function callDelegationManager(bytes calldata data) external payable onlyOwner { - _executeCall(address(eigenLayerDelegationManager), msg.value, data); - } - - /// @notice Call the Eigenlayer Manager contract - /// @param data to call contract - function callEigenPodManager(bytes calldata data) external payable onlyOwner { - _executeCall(address(eigenLayerPodManager), msg.value, data); - } - - /// @notice Withdraw funds from the delayed withdrawal router - /// @param numberOfDelayedWithdrawalsToClaim number of claims - function claimDelayedWithdrawals(uint256 numberOfDelayedWithdrawalsToClaim) external { - delayedWithdrawalRouter.claimDelayedWithdrawals(address(this), numberOfDelayedWithdrawalsToClaim); - - // transfer eth to withdrawalAddress - uint256 balance = address(this).balance; - if (feeShare > 0) { - uint256 fee = (balance * feeShare) / PERCENTAGE_SCALE; - feeRecipient.safeTransferETH(fee); - withdrawalAddress.safeTransferETH(balance -= fee); - } else { - withdrawalAddress.safeTransferETH(balance); - } - } - - /// @notice Rescue stuck tokens by sending them to the split contract. - /// @param token address of token - /// @param amount amount of token to rescue - function rescueFunds(address token, uint256 amount) external { - if (amount > 0) ERC20(token).safeTransfer(withdrawalAddress, amount); - } - - /// @notice Execute a low level call - /// @param to address to execute call - /// @param value amount of ETH to send with call - /// @param data bytes array to execute - function _executeCall(address to, uint256 value, bytes memory data) internal { - (bool success,) = address(to).call{value: value}(data); - if (!success) revert CallFailed(data); - } -} diff --git a/src/eigenlayer/ObolEigenLayerPodControllerFactory.sol b/src/eigenlayer/ObolEigenLayerPodControllerFactory.sol deleted file mode 100644 index 2716e01..0000000 --- a/src/eigenlayer/ObolEigenLayerPodControllerFactory.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.19; - -import {ObolEigenLayerPodController} from "./ObolEigenLayerPodController.sol"; -import {LibClone} from "solady/utils/LibClone.sol"; - -/// @title ObolEigenLayerFactory -/// @author Obol -/// @notice A factory contract for cheaply deploying ObolLidoEigenLayer. -/// @dev The address returned should be used to as the EigenPod address -contract ObolEigenLayerPodControllerFactory { - error Invalid_Owner(); - error Invalid_WithdrawalAddress(); - error Invalid_DelegationManager(); - error Invalid_EigenPodManaager(); - error Invalid_WithdrawalRouter(); - - using LibClone for address; - - event CreatePodController(address indexed controller, address indexed withdrawalAddress, address owner); - - ObolEigenLayerPodController public immutable controllerImplementation; - - constructor( - address feeRecipient, - uint256 feeShare, - address delegationManager, - address eigenPodManager, - address withdrawalRouter - ) { - if (delegationManager == address(0)) revert Invalid_DelegationManager(); - if (eigenPodManager == address(0)) revert Invalid_EigenPodManaager(); - if (withdrawalRouter == address(0)) revert Invalid_WithdrawalRouter(); - - controllerImplementation = - new ObolEigenLayerPodController(feeRecipient, feeShare, delegationManager, eigenPodManager, withdrawalRouter); - // initialize implementation - controllerImplementation.initialize(feeRecipient, feeRecipient); - } - - /// Creates a minimal proxy clone of implementation - /// @param owner address of owner - /// @param withdrawalAddress address of withdrawalAddress - /// @return controller Deployed obol eigen layer controller - function createPodController(address owner, address withdrawalAddress) external returns (address controller) { - if (owner == address(0)) revert Invalid_Owner(); - if (withdrawalAddress == address(0)) revert Invalid_WithdrawalAddress(); - - bytes32 salt = _createSalt(owner, withdrawalAddress); - - controller = address(controllerImplementation).cloneDeterministic("", salt); - - ObolEigenLayerPodController(controller).initialize(owner, withdrawalAddress); - - emit CreatePodController(controller, withdrawalAddress, owner); - } - - /// Predict the controller address - /// @param owner address of owner - /// @param withdrawalAddress address to withdraw funds to - function predictControllerAddress(address owner, address withdrawalAddress) - external - view - returns (address controller) - { - bytes32 salt = _createSalt(owner, withdrawalAddress); - controller = address(controllerImplementation).predictDeterministicAddress("", salt, address(this)); - } - - function _createSalt(address owner, address withdrawalAddress) internal pure returns (bytes32 salt) { - return keccak256(abi.encode(owner, withdrawalAddress)); - } -} diff --git a/src/interfaces/IEigenLayer.sol b/src/interfaces/IEigenLayer.sol deleted file mode 100644 index 48e562a..0000000 --- a/src/interfaces/IEigenLayer.sol +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; -import {ERC20} from "solmate/tokens/ERC20.sol"; - -interface IEigenLayerUtils { - // @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; - } -} - -interface IDelegationManager is IEigenLayerUtils { - - /** - * @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: - * 1) the operator's `delegationApprover` address is set to a non-zero value. - * AND - * 2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator - * or their delegationApprover is the `msg.sender`, then approval is assumed. - * @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 - */ - function delegateTo( - address operator, - SignatureWithExpiry memory approverSignatureAndExpiry, - bytes32 approverSalt - ) external; - - /** - * @notice Undelegates the staker from the operator who they are delegated to. Puts the staker into the "undelegation limbo" mode of the EigenPodManager - * and queues a withdrawal of all of the staker's shares in the StrategyManager (to the staker), if necessary. - * @param staker The account to be undelegated. - * @return withdrawalRoot The root of the newly queued withdrawal, 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 withdrawalRoot); -} - -interface IEigenPodManager { - /** - * @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 Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not). - function getPod(address podOwner) external returns (address); -} - - -interface IDelayedWithdrawalRouter { - /** - * @notice Called in order to withdraw delayed withdrawals made to the `recipient` that have passed the `withdrawalDelayBlocks` period. - * @param recipient The address to claim delayedWithdrawals for. - * @param maxNumberOfDelayedWithdrawalsToClaim Used to limit the maximum number of delayedWithdrawals to loop through claiming. - * @dev - * WARNING: Note that the caller of this function cannot control where the funds are sent, but they can control when the - * funds are sent once the withdrawal becomes claimable. - */ - function claimDelayedWithdrawals( - address recipient, - uint256 maxNumberOfDelayedWithdrawalsToClaim - ) external; - - /** - * @notice Creates a delayed withdrawal for `msg.value` to the `recipient`. - * @dev Only callable by the `podOwner`'s EigenPod contract. - */ - function createDelayedWithdrawal( - address podOwner, - address recipient - ) external; - - /// @notice Owner-only function for modifying the value of the `withdrawalDelayBlocks` variable. - function setWithdrawalDelayBlocks(uint256 newValue) external; -} - -interface IEigenPod { - - function activateRestaking() external; - - /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false - function withdrawBeforeRestaking() external; - - /// @notice Called by the pod owner to withdraw the nonBeaconChainETHBalanceWei - function withdrawNonBeaconChainETHBalanceWei(address recipient, uint256 amountToWithdraw) external; - - /// @notice called by owner of a pod to remove any ERC20s deposited in the pod - function recoverTokens(ERC20[] memory tokenList, uint256[] memory amountsToWithdraw, address recipient) external; - - /// @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 an indicator of whether or not the podOwner has ever "fully restaked" by successfully calling `verifyCorrectWithdrawalCredentials`. - function hasRestaked() external view returns (bool); - - /// @notice The max amount of eth, in gwei, that can be restaked per validator - function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64); - - /// @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 any ETH deposited into the EigenPod contract via the `receive` fallback function - function nonBeaconChainETHBalanceWei() external view returns (uint256); - - /// @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; -} \ No newline at end of file diff --git a/src/test/eigenlayer/EigenLayerTestBase.sol b/src/test/eigenlayer/EigenLayerTestBase.sol deleted file mode 100644 index f0aa775..0000000 --- a/src/test/eigenlayer/EigenLayerTestBase.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; -import { - IEigenPod, - IDelegationManager, - IEigenPodManager, - IEigenLayerUtils, - IDelayedWithdrawalRouter -} from "src/interfaces/IEigenLayer.sol"; - -abstract contract EigenLayerTestBase is Test { - uint256 public constant PERCENTAGE_SCALE = 1e5; - - address public constant SPLIT_MAIN_GOERLI = 0x2ed6c4B5dA6378c7897AC67Ba9e43102Feb694EE; - - address public constant ENS_REVERSE_REGISTRAR_GOERLI = 0x084b1c3C81545d370f3634392De611CaaBFf8148; - - address public constant DEPOSIT_CONTRACT_GOERLI = 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b; - address public constant DELEGATION_MANAGER_GOERLI = 0x1b7b8F6b258f95Cf9596EabB9aa18B62940Eb0a8; - address public constant POD_MANAGER_GOERLI = 0xa286b84C96aF280a49Fe1F40B9627C2A2827df41; - address public constant DELAY_ROUTER_GOERLI = 0x89581561f1F98584F88b0d57c2180fb89225388f; - // eigenlayer admin - address public constant DELAY_ROUTER_OWNER_GOERLI = 0x37bAFb55BC02056c5fD891DFa503ee84a97d89bF; - address public constant EIGEN_LAYER_OPERATOR_GOERLI = 0x3DeD1CB5E25FE3eC9811B918A809A371A4965A5D; - - uint256 internal constant BALANCE_CLASSIFICATION_THRESHOLD = 16 ether; - - function encodeEigenPodCall(address recipient, uint256 amount) internal pure returns (bytes memory callData) { - callData = abi.encodeCall(IEigenPod.withdrawNonBeaconChainETHBalanceWei, (recipient, amount)); - } - - function encodeDelegationManagerCall(address operator) internal pure returns (bytes memory callData) { - IEigenLayerUtils.SignatureWithExpiry memory signature = IEigenLayerUtils.SignatureWithExpiry(bytes(""), 0); - callData = abi.encodeCall(IDelegationManager.delegateTo, (operator, signature, bytes32(0))); - } - - function encodeEigenPodManagerCall(uint256) internal pure returns (bytes memory callData) { - bytes memory pubkey = bytes(""); - bytes memory signature = bytes(""); - bytes32 dataRoot = bytes32(0); - - callData = abi.encodeCall(IEigenPodManager.stake, (pubkey, signature, dataRoot)); - } - - function _min(uint256 a, uint256 b) internal pure returns (uint256 min) { - min = a > b ? b : a; - } - - function _max(uint256 a, uint256 b) internal pure returns (uint256 max) { - max = a > b ? a : b; - } -} diff --git a/src/test/eigenlayer/OELPCFactory.t.sol.obsolete b/src/test/eigenlayer/OELPCFactory.t.sol.obsolete deleted file mode 100644 index 5323e0b..0000000 --- a/src/test/eigenlayer/OELPCFactory.t.sol.obsolete +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.19; - -import "forge-std/Test.sol"; -import {ObolEigenLayerPodControllerFactory} from "src/eigenlayer/ObolEigenLayerPodControllerFactory.sol"; -import {EigenLayerTestBase} from "./EigenLayerTestBase.sol"; - -contract ObolEigenLayerPodControllerFactoryTest is EigenLayerTestBase { - error Invalid_Owner(); - error Invalid_WithdrawalAddress(); - error Invalid_DelegationManager(); - error Invalid_EigenPodManaager(); - error Invalid_WithdrawalRouter(); - - event CreatePodController(address indexed controller, address indexed split, address owner); - - ObolEigenLayerPodControllerFactory factory; - - address owner; - address user1; - address withdrawalAddress; - address feeRecipient; - - uint256 feeShare; - - function setUp() public { - vm.createSelectFork(getChain("goerli").rpcUrl); - - owner = makeAddr("owner"); - user1 = makeAddr("user1"); - withdrawalAddress = makeAddr("withdrawalAddress"); - feeRecipient = makeAddr("feeRecipient"); - feeShare = 1e3; - - factory = new ObolEigenLayerPodControllerFactory( - feeRecipient, feeShare, DELEGATION_MANAGER_GOERLI, POD_MANAGER_GOERLI, DELAY_ROUTER_GOERLI - ); - } - - function test_RevertIfInvalidDelegationManger() external { - vm.expectRevert(Invalid_DelegationManager.selector); - new ObolEigenLayerPodControllerFactory(feeRecipient, feeShare, address(0), POD_MANAGER_GOERLI, DELAY_ROUTER_GOERLI); - } - - function test_RevertIfInvalidPodManger() external { - vm.expectRevert(Invalid_EigenPodManaager.selector); - new ObolEigenLayerPodControllerFactory( - feeRecipient, feeShare, DELEGATION_MANAGER_GOERLI, address(0), DELAY_ROUTER_GOERLI - ); - } - - function test_RevertIfInvalidWithdrawalRouter() external { - vm.expectRevert(Invalid_WithdrawalRouter.selector); - new ObolEigenLayerPodControllerFactory( - feeRecipient, feeShare, DELEGATION_MANAGER_GOERLI, POD_MANAGER_GOERLI, address(0) - ); - } - - function test_RevertIfOwnerIsZero() external { - vm.expectRevert(Invalid_Owner.selector); - factory.createPodController(address(0), withdrawalAddress); - } - - function test_RevertIfOWRIsZero() external { - vm.expectRevert(Invalid_WithdrawalAddress.selector); - factory.createPodController(user1, address(0)); - } - - function test_CreatePodController() external { - vm.expectEmit(false, false, false, true); - - emit CreatePodController(address(0), withdrawalAddress, user1); - - address predictedAddress = factory.predictControllerAddress( - user1, - withdrawalAddress - ); - - address createdAddress = factory.createPodController(user1, withdrawalAddress); - - assertEq( - predictedAddress, - createdAddress, - "predicted address is equivalent" - ); - } -} diff --git a/src/test/eigenlayer/ObolEigenLayerPodController.t.sol.obsolete b/src/test/eigenlayer/ObolEigenLayerPodController.t.sol.obsolete deleted file mode 100644 index b761bc7..0000000 --- a/src/test/eigenlayer/ObolEigenLayerPodController.t.sol.obsolete +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; -import {ObolEigenLayerPodController} from "src/eigenlayer/ObolEigenLayerPodController.sol"; -import {ObolEigenLayerPodControllerFactory} from "src/eigenlayer/ObolEigenLayerPodControllerFactory.sol"; -import { - IEigenPod, - IDelegationManager, - IEigenPodManager, - IEigenLayerUtils, - IDelayedWithdrawalRouter -} from "src/interfaces/IEigenLayer.sol"; -import {MockERC20} from "src/test/utils/mocks/MockERC20.sol"; -import { - OptimisticWithdrawalRecipientFactory, - OptimisticWithdrawalRecipient -} from "src/owr/OptimisticWithdrawalRecipientFactory.sol"; -import {EigenLayerTestBase} from "./EigenLayerTestBase.sol"; - -interface IDepositContract { - function deposit( - bytes calldata pubkey, - bytes calldata withdrawal_credentials, - bytes calldata signature, - bytes32 deposit_data_root - ) external payable; -} - -contract ObolEigenLayerPodControllerTest is EigenLayerTestBase { - error Unauthorized(); - error AlreadyInitialized(); - error Invalid_FeeShare(); - error CallFailed(bytes); - - ObolEigenLayerPodControllerFactory factory; - ObolEigenLayerPodControllerFactory zeroFeeFactory; - - ObolEigenLayerPodController controller; - ObolEigenLayerPodController zeroFeeController; - - address owner; - address user1; - address user2; - address withdrawalAddress; - address principalRecipient; - address feeRecipient; - - uint256 feeShare; - - MockERC20 mERC20; - - function setUp() public { - vm.createSelectFork(getChain("goerli").rpcUrl); - - vm.mockCall( - DEPOSIT_CONTRACT_GOERLI, abi.encodeWithSelector(IDepositContract.deposit.selector), bytes.concat(bytes32(0)) - ); - - owner = makeAddr("owner"); - user1 = makeAddr("user1"); - user1 = makeAddr("user2"); - principalRecipient = makeAddr("principalRecipient"); - withdrawalAddress = makeAddr("withdrawalAddress"); - feeRecipient = makeAddr("feeRecipient"); - feeShare = 1e3; - - factory = new ObolEigenLayerPodControllerFactory( - feeRecipient, feeShare, DELEGATION_MANAGER_GOERLI, POD_MANAGER_GOERLI, DELAY_ROUTER_GOERLI - ); - - zeroFeeFactory = new ObolEigenLayerPodControllerFactory( - address(0), 0, DELEGATION_MANAGER_GOERLI, POD_MANAGER_GOERLI, DELAY_ROUTER_GOERLI - ); - - controller = ObolEigenLayerPodController(factory.createPodController(owner, withdrawalAddress)); - zeroFeeController = ObolEigenLayerPodController(zeroFeeFactory.createPodController(owner, withdrawalAddress)); - - mERC20 = new MockERC20("Test Token", "TOK", 18); - mERC20.mint(type(uint256).max); - - vm.prank(DELAY_ROUTER_OWNER_GOERLI); - // set the delay withdrawal duration to zero - IDelayedWithdrawalRouter(DELAY_ROUTER_GOERLI).setWithdrawalDelayBlocks(0); - } - - function test_RevertIfInvalidFeeShare() external { - vm.expectRevert(Invalid_FeeShare.selector); - new ObolEigenLayerPodControllerFactory( - feeRecipient, 1e7, DELEGATION_MANAGER_GOERLI, POD_MANAGER_GOERLI, DELAY_ROUTER_GOERLI - ); - } - - function test_RevertIfNotOwnerCallEigenPod() external { - vm.prank(user1); - vm.expectRevert(Unauthorized.selector); - controller.callEigenPod(encodeEigenPodCall(user1, 1 ether)); - } - - function test_RevertIfDoubleInitialize() external { - vm.prank(user1); - vm.expectRevert(AlreadyInitialized.selector); - controller.initialize(owner, withdrawalAddress); - } - - function test_CallEigenPod() external { - address pod = controller.eigenPod(); - uint256 amount = 1 ether; - - // airdrop ether to pod - (bool success,) = pod.call{value: amount}(""); - require(success, "call failed"); - - vm.prank(owner); - controller.callEigenPod(encodeEigenPodCall(user1, amount)); - } - - function test_CallDelegationManager() external { - vm.prank(owner); - controller.callDelegationManager(encodeDelegationManagerCall(EIGEN_LAYER_OPERATOR_GOERLI)); - } - - function test_OnlyOwnerCallDelegationManager() external { - vm.prank(user1); - vm.expectRevert(Unauthorized.selector); - controller.callDelegationManager(encodeDelegationManagerCall(EIGEN_LAYER_OPERATOR_GOERLI)); - } - - function test_CallEigenPodManager() external { - uint256 etherStake = 32 ether; - vm.deal(owner, etherStake + 1 ether); - vm.prank(owner); - controller.callEigenPodManager{value: etherStake}(encodeEigenPodManagerCall(0)); - } - - function test_OnlyOwnerEigenPodManager() external { - vm.expectRevert(Unauthorized.selector); - controller.callEigenPodManager(encodeEigenPodManagerCall(0)); - } - - function test_ClaimDelayedWithdrawals() external { - uint256 amountToDeposit = 2 ether; - - // transfer unstake beacon eth to eigenPod - (bool success,) = address(controller.eigenPod()).call{value: amountToDeposit}(""); - require(success, "call failed"); - - vm.startPrank(owner); - { - controller.callEigenPod(encodeEigenPodCall(address(controller), amountToDeposit)); - controller.claimDelayedWithdrawals(1); - } - vm.stopPrank(); - - assertEq(address(feeRecipient).balance, 20_000_000_000_000_000, "fee recipient balance increased"); - assertEq(address(withdrawalAddress).balance, 1_980_000_000_000_000_000, "withdrawal balance increased"); - } - - function test_ClaimDelayedWithdrawalsZeroFee() external { - uint256 amountToDeposit = 20 ether; - - // transfer unstake beacon eth to eigenPod - (bool success,) = address(zeroFeeController.eigenPod()).call{value: amountToDeposit}(""); - require(success, "call failed"); - - vm.startPrank(owner); - { - zeroFeeController.callEigenPod(encodeEigenPodCall(address(zeroFeeController), amountToDeposit)); - zeroFeeController.claimDelayedWithdrawals(1); - } - vm.stopPrank(); - - assertEq(address(withdrawalAddress).balance, amountToDeposit, "withdrawal balance increased"); - } - - function test_InvalidCallReverts() external { - uint256 amountToDeposit = 20 ether; - bytes memory data = encodeEigenPodCall(address(0x2), amountToDeposit); - vm.expectRevert(abi.encodeWithSelector(CallFailed.selector, data)); - vm.prank(owner); - zeroFeeController.callEigenPod(data); - vm.stopPrank(); - } - - function testFuzz_ClaimDelayedWithdrawals(uint256 amount) external { - amount = bound(amount, _min(amount, address(this).balance), type(uint96).max); - - address DELAY_ROUTER_OWNER = 0x37bAFb55BC02056c5fD891DFa503ee84a97d89bF; - vm.prank(DELAY_ROUTER_OWNER); - // set the delay withdrawal duration to zero - IDelayedWithdrawalRouter(DELAY_ROUTER_GOERLI).setWithdrawalDelayBlocks(0); - - // transfer unstake beacon eth to eigenPod - (bool success,) = address(controller.eigenPod()).call{value: amount}(""); - require(success, "call failed"); - - vm.startPrank(owner); - { - controller.callEigenPod(encodeEigenPodCall(address(controller), amount)); - controller.claimDelayedWithdrawals(1); - } - vm.stopPrank(); - - uint256 fee = amount * feeShare / PERCENTAGE_SCALE; - - assertEq(address(feeRecipient).balance, fee, "invalid fee"); - - assertEq(address(withdrawalAddress).balance, amount -= fee, "invalid withdrawalAddress balance"); - } - - function test_RescueFunds() external { - uint256 amount = 1e18; - mERC20.transfer(address(controller), amount); - - controller.rescueFunds(address(mERC20), amount); - - assertEq(mERC20.balanceOf(withdrawalAddress), amount, "could not rescue funds"); - } - - function test_RescueFundsZero() external { - uint256 amount = 0; - controller.rescueFunds(address(mERC20), amount); - - assertEq(mERC20.balanceOf(withdrawalAddress), amount, "balance should be zero"); - } -} diff --git a/src/test/eigenlayer/integration/OELPCIntegration.t.sol.obsolete b/src/test/eigenlayer/integration/OELPCIntegration.t.sol.obsolete deleted file mode 100644 index 34ec4fe..0000000 --- a/src/test/eigenlayer/integration/OELPCIntegration.t.sol.obsolete +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; -import {ObolEigenLayerPodController} from "src/eigenlayer/ObolEigenLayerPodController.sol"; -import {ObolEigenLayerPodControllerFactory} from "src/eigenlayer/ObolEigenLayerPodControllerFactory.sol"; -import { - IEigenPod, - IDelegationManager, - IEigenPodManager, - IEigenLayerUtils, - IDelayedWithdrawalRouter -} from "src/interfaces/IEigenLayer.sol"; -import {MockERC20} from "src/test/utils/mocks/MockERC20.sol"; -import {ISplitMain} from "src/interfaces/ISplitMain.sol"; -import { - OptimisticWithdrawalRecipientFactory, - OptimisticWithdrawalRecipient -} from "src/owr/OptimisticWithdrawalRecipientFactory.sol"; -import {IENSReverseRegistrar} from "../../../interfaces/IENSReverseRegistrar.sol"; -import {EigenLayerTestBase} from "../EigenLayerTestBase.sol"; - -contract OELPCIntegration is EigenLayerTestBase { - ObolEigenLayerPodControllerFactory factory; - ObolEigenLayerPodController owrController; - ObolEigenLayerPodController splitController; - - address[] accounts; - uint32[] percentAllocations; - - address owner; - address user1; - address user2; - - address owrWithdrawalAddress; - address splitWithdrawalAddress; - - address principalRecipient; - address rewardRecipient; - address feeRecipient; - - uint256 feeShare; - - function setUp() public { - vm.createSelectFork(getChain("goerli").rpcUrl); - - vm.mockCall( - ENS_REVERSE_REGISTRAR_GOERLI, - abi.encodeWithSelector(IENSReverseRegistrar.setName.selector), - bytes.concat(bytes32(0)) - ); - vm.mockCall( - ENS_REVERSE_REGISTRAR_GOERLI, - abi.encodeWithSelector(IENSReverseRegistrar.claim.selector), - bytes.concat(bytes32(0)) - ); - - owner = makeAddr("owner"); - user1 = makeAddr("user1"); - user1 = makeAddr("user2"); - principalRecipient = makeAddr("principalRecipient"); - rewardRecipient = makeAddr("rewardRecipient"); - feeRecipient = makeAddr("feeRecipient"); - feeShare = 1e3; - - OptimisticWithdrawalRecipientFactory owrFactory = - new OptimisticWithdrawalRecipientFactory("demo.obol.eth", ENS_REVERSE_REGISTRAR_GOERLI, address(this)); - - owrWithdrawalAddress = - address(owrFactory.createOWRecipient(address(0), principalRecipient, rewardRecipient, 32 ether)); - - factory = new ObolEigenLayerPodControllerFactory( - feeRecipient, feeShare, DELEGATION_MANAGER_GOERLI, POD_MANAGER_GOERLI, DELAY_ROUTER_GOERLI - ); - - owrController = ObolEigenLayerPodController(factory.createPodController(owner, owrWithdrawalAddress)); - - accounts = new address[](2); - accounts[0] = makeAddr("accounts0"); - accounts[1] = makeAddr("accounts1"); - - percentAllocations = new uint32[](2); - percentAllocations[0] = 300_000; - percentAllocations[1] = 700_000; - - splitWithdrawalAddress = ISplitMain(SPLIT_MAIN_GOERLI).createSplit(accounts, percentAllocations, 0, address(0)); - - splitController = ObolEigenLayerPodController(factory.createPodController(owner, splitWithdrawalAddress)); - - vm.prank(DELAY_ROUTER_OWNER_GOERLI); - // set the delay withdrawal duration to zero - IDelayedWithdrawalRouter(DELAY_ROUTER_GOERLI).setWithdrawalDelayBlocks(0); - } - - function testFuzz_WithdrawOWR(uint256 amountToDeposit) external { - vm.assume(amountToDeposit > 0); - - uint256 stakeSize = 32 ether; - - amountToDeposit = boundETH(amountToDeposit); - // transfer unstake beacon eth to eigenPod - (bool success,) = address(owrController.eigenPod()).call{value: amountToDeposit}(""); - require(success, "call failed"); - - vm.startPrank(owner); - { - owrController.callEigenPod(encodeEigenPodCall(address(owrController), amountToDeposit)); - owrController.claimDelayedWithdrawals(1); - } - vm.stopPrank(); - - uint256 fee = amountToDeposit * feeShare / PERCENTAGE_SCALE; - - assertEq(address(feeRecipient).balance, fee, "fee recipient balance increased"); - - uint256 owrBalance = amountToDeposit - fee; - assertEq(address(owrWithdrawalAddress).balance, owrBalance, "owr balance increased"); - - // call distribute on owrWithdrawal address - OptimisticWithdrawalRecipient(owrWithdrawalAddress).distributeFunds(); - - // check the princiapl recipient - if (owrBalance >= BALANCE_CLASSIFICATION_THRESHOLD) { - if (owrBalance > stakeSize) { - // prinicipal rexeives 32 eth and reward recieves remainder - assertEq(address(principalRecipient).balance, stakeSize, "invalid principal balance"); - assertEq(address(rewardRecipient).balance, owrBalance - stakeSize, "invalid reward balance"); - } else { - // principal receives everything - assertEq(address(principalRecipient).balance, owrBalance, "invalid principal balance"); - } - } else { - // reward recipient receives everything - assertEq(address(rewardRecipient).balance, owrBalance, "invalid reward balance"); - } - } - - function testFuzz_WithdrawSplit(uint256 amountToDeposit) external { - vm.assume(amountToDeposit > 0); - - amountToDeposit = boundETH(amountToDeposit); - // transfer unstake beacon eth to eigenPod - (bool success,) = address(splitController.eigenPod()).call{value: amountToDeposit}(""); - require(success, "call failed"); - - vm.startPrank(owner); - { - splitController.callEigenPod(encodeEigenPodCall(address(splitController), amountToDeposit)); - splitController.claimDelayedWithdrawals(1); - } - vm.stopPrank(); - - uint256 fee = amountToDeposit * feeShare / PERCENTAGE_SCALE; - assertEq(address(feeRecipient).balance, fee, "fee recipient balance increased"); - - uint256 splitBalance = amountToDeposit - fee; - - assertEq(address(splitWithdrawalAddress).balance, splitBalance, "invalid balance"); - } - - function boundETH(uint256 amount) internal view returns (uint256 result) { - result = bound(amount, 1, type(uint96).max); - } -}