Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ObolValidatorManager #154

Merged
merged 6 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,31 @@
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import {OptimisticWithdrawalRecipientV2Factory} from "../src/owr/OptimisticWithdrawalRecipientV2Factory.sol";
import {ObolValidatorManagerFactory} from "../src/ovm/ObolValidatorManagerFactory.sol";

//
// This script creates a new OptimisticWithdrawalRecipientV2 contract.
// This script creates a new ObolValidatorManager contract.
// To run this script, the following environment variables must be set:
// - PRIVATE_KEY: the private key of the account that will deploy the contract
// Example usage:
// forge script script/CreateOWRecipientScript.s.sol --sig "run(address)" \
// forge script script/CreateObolValidatorManagerScript.s.sol --sig "run(address)" \
// --rpc-url https://rpc.pectra-devnet-5.ethpandaops.io/ --broadcast "<factory_address>"
//
contract CreateOWRecipientScript is Script {
contract CreateObolValidatorManagerScript is Script {
function run(address deployedOWRV2Factory) external {
uint256 privKey = vm.envUint("PRIVATE_KEY");

vm.startBroadcast(privKey);

OptimisticWithdrawalRecipientV2Factory factory = OptimisticWithdrawalRecipientV2Factory(deployedOWRV2Factory);
ObolValidatorManagerFactory factory = ObolValidatorManagerFactory(deployedOWRV2Factory);

address owner = msg.sender;
address recoveryAddress = msg.sender;
address principalRecipient = msg.sender;
address rewardsRecipient = msg.sender;
uint64 principalThreshold = 16 ether / 1 gwei;

// Call the createOWRecipient function
factory.createOWRecipient(
factory.createObolValidatorManager(
owner,
principalRecipient,
rewardsRecipient,
Expand Down
8 changes: 4 additions & 4 deletions script/DistributeFunds.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import {OptimisticWithdrawalRecipientV2} from "src/owr/OptimisticWithdrawalRecipientV2.sol";
import {ObolValidatorManager} from "src/ovm/ObolValidatorManager.sol";

//
// This script calls distributeFunds() for a OptimisticWithdrawalRecipientV2 contract.
// This script calls distributeFunds() for a ObolValidatorManager contract.
// To run this script, the following environment variables must be set:
// - PRIVATE_KEY: the private key of the account that will deploy the contract
// Example usage:
Expand All @@ -18,8 +18,8 @@ contract DistributeFunds is Script {

vm.startBroadcast(privKey);

OptimisticWithdrawalRecipientV2 owr = OptimisticWithdrawalRecipientV2(payable(deployedOWRV2));
owr.distributeFunds();
ObolValidatorManager ovm = ObolValidatorManager(payable(deployedOWRV2));
ovm.distributeFunds();

vm.stopBroadcast();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import {OptimisticWithdrawalRecipientV2Factory} from "src/owr/OptimisticWithdrawalRecipientV2Factory.sol";
import "forge-std/console.sol";
import {ObolValidatorManagerFactory} from "src/ovm/ObolValidatorManagerFactory.sol";

//
// This script deploys a new OptimisticWithdrawalRecipientV2Factory contract.
// This script deploys a new ObolValidatorManagerFactory contract.
// To run this script, the following environment variables must be set:
// - PRIVATE_KEY: the private key of the account that will deploy the contract
// Please set ensReverseRegistrar before running this script.
// Example usage:
// forge script script/OWRV2FactoryScript.s.sol --sig "run(string)"
// forge script script/ObolValidatorManagerFactoryScript.s.sol --sig "run(string)"
// --rpc-url https://rpc.pectra-devnet-5.ethpandaops.io/ --broadcast "demo"
//
contract OWRV2FactoryScript is Script {
contract ObolValidatorManagerFactoryScript is Script {
// From https://github.com/ethereum/EIPs/blob/d96625a4dcbbe2572fa006f062bd02b4582eefd5/EIPS/eip-7251.md#execution-layer
address constant consolidationSysContract = 0x00431F263cE400f4455c2dCf564e53007Ca4bbBb;
// From https://github.com/ethereum/EIPs/blob/d96625a4dcbbe2572fa006f062bd02b4582eefd5/EIPS/eip-7002.md#configuration
Expand All @@ -36,7 +37,7 @@ contract OWRV2FactoryScript is Script {

vm.startBroadcast(privKey);

new OptimisticWithdrawalRecipientV2Factory{salt: keccak256(bytes(name))}(
ObolValidatorManagerFactory factory = new ObolValidatorManagerFactory{salt: keccak256(bytes(name))}(
consolidationSysContract,
withdrawalSysContract,
depositSysContract,
Expand All @@ -45,6 +46,8 @@ contract OWRV2FactoryScript is Script {
msg.sender
);

console.log('ObolValidatorManagerFactory deployed at: ', address(factory));

vm.stopBroadcast();
}
}
8 changes: 4 additions & 4 deletions script/RequestConsolidation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import {OptimisticWithdrawalRecipientV2} from "src/owr/OptimisticWithdrawalRecipientV2.sol";
import {ObolValidatorManager} from "src/ovm/ObolValidatorManager.sol";

//
// This script calls requestConsolidation() for a OptimisticWithdrawalRecipientV2 contract.
// This script calls requestConsolidation() for a ObolValidatorManager contract.
// To run this script, the following environment variables must be set:
// - PRIVATE_KEY: the private key of the account that will deploy the contract
// Example usage:
Expand All @@ -19,14 +19,14 @@ contract RequestConsolidation is Script {

vm.startBroadcast(privKey);

OptimisticWithdrawalRecipientV2 owr = OptimisticWithdrawalRecipientV2(payable(owrv2));
ObolValidatorManager ovm = ObolValidatorManager(payable(owrv2));

// Call the function on the deployed contract
bytes[] memory sourcePubKeys = new bytes[](1);
sourcePubKeys[0] = src;

// Estimated total gas used for script: 162523
owr.requestConsolidation{value: 100 wei}(sourcePubKeys, dst);
ovm.requestConsolidation{value: 100 wei}(sourcePubKeys, dst);

vm.stopBroadcast();
}
Expand Down
8 changes: 4 additions & 4 deletions script/RequestWithdrawal.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import {OptimisticWithdrawalRecipientV2} from "src/owr/OptimisticWithdrawalRecipientV2.sol";
import {ObolValidatorManager} from "src/ovm/ObolValidatorManager.sol";

//
// This script calls requestWithdrawal() for a OptimisticWithdrawalRecipientV2 contract.
// This script calls requestWithdrawal() for a ObolValidatorManager contract.
// To run this script, the following environment variables must be set:
// - PRIVATE_KEY: the private key of the account that will deploy the contract
// Example usage:
Expand All @@ -19,7 +19,7 @@ contract RequestWithdrawal is Script {

vm.startBroadcast(privKey);

OptimisticWithdrawalRecipientV2 owr = OptimisticWithdrawalRecipientV2(payable(owrv2));
ObolValidatorManager ovm = ObolValidatorManager(payable(owrv2));

bytes[] memory pubKeys = new bytes[](1);
pubKeys[0] = pubkey;
Expand All @@ -28,7 +28,7 @@ contract RequestWithdrawal is Script {
amounts[0] = amount;

// Estimated total gas used for script: 219325
owr.requestWithdrawal{value: 100 wei}(pubKeys, amounts);
ovm.requestWithdrawal{value: 100 wei}(pubKeys, amounts);

vm.stopBroadcast();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import {OwnableRoles} from "solady/auth/OwnableRoles.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {IDepositContract} from "../interfaces/IDepositContract.sol";

/// @title OptimisticWithdrawalRecipientV2
/// @title ObolValidatorManager
/// @author Obol
/// @notice A maximally-composable contract that distributes payments
/// based on threshold to it's recipients.
/// @dev Only ETH can be distributed for a given deployment. There is a
/// recovery method for tokens sent by accident.
contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
contract ObolValidatorManager is OwnableRoles {
/// -----------------------------------------------------------------------
/// libraries
/// -----------------------------------------------------------------------
Expand All @@ -23,9 +23,6 @@ contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
/// errors
/// -----------------------------------------------------------------------

// The instance is already initialized
error Invalid_AlreadyInitialized();

// Invalid request params, e.g. empty input
error InvalidRequest_Params();

Expand Down Expand Up @@ -109,8 +106,6 @@ contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// @dev set to `true` after owner is initialized
bool public initialized;

/// Amount of principal stake (wei) done via deposit() calls
uint256 public amountOfPrincipalStake;
Expand All @@ -130,6 +125,7 @@ contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
address _consolidationSystemContract,
address _withdrawalSystemContract,
address _depositSystemContract,
address _owner,
address _principalRecipient,
address _rewardRecipient,
address _recoveryAddress,
Expand All @@ -142,6 +138,8 @@ contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
rewardRecipient = _rewardRecipient;
recoveryAddress = _recoveryAddress;
principalThreshold = _principalThreshold;

_initializeOwner(_owner);
}

/// -----------------------------------------------------------------------
Expand All @@ -152,14 +150,6 @@ contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
/// functions - public & external
/// -----------------------------------------------------------------------

/// @dev initializes the owner
/// @param _owner the owner address
function initialize(address _owner) public {
if (initialized) revert Invalid_AlreadyInitialized();
_initializeOwner(_owner);
initialized = true;
}

/// @dev Fallback function to receive ETH
/// Because we do not use Clone, we must implement this explicitly
receive() external payable {}
Expand Down Expand Up @@ -241,7 +231,9 @@ contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
/// withdrawals that leave a validator with (0..32) ether,
// will only withdraw an amount that leaves the validator at 32 ether
/// @param pubKeys validator public keys
/// @param amounts withdrawal amounts in gwei (must be >= principalThreshold)
/// @param amounts withdrawal amounts in gwei
/// any amount below principalThreshold will be distributed as reward
/// any amount >= principalThreshold will be distributed as principal
function requestWithdrawal(
bytes[] calldata pubKeys,
uint64[] calldata amounts
Expand All @@ -252,8 +244,6 @@ contract OptimisticWithdrawalRecipientV2 is OwnableRoles {
uint256 len = pubKeys.length;

for (uint256 i; i < len; ) {
if (amounts[i] < principalThreshold) revert InvalidRequest_Params();

uint256 _currentFee = _computeSystemContractFee(withdrawalSystemContract);
if (_currentFee > remainingFee) revert InvalidRequest_NotEnoughFee();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;

import {OptimisticWithdrawalRecipientV2} from "./OptimisticWithdrawalRecipientV2.sol";
import {ObolValidatorManager} from "./ObolValidatorManager.sol";
import {IENSReverseRegistrar} from "../interfaces/IENSReverseRegistrar.sol";

/// @title OptimisticWithdrawalRecipientV2Factory
/// @title ObolValidatorManagerFactory
/// @author Obol
/// @notice A factory contract for deploying OptimisticWithdrawalRecipientV2.
contract OptimisticWithdrawalRecipientV2Factory {
/// @notice A factory contract for deploying ObolValidatorManager.
contract ObolValidatorManagerFactory {
/// -----------------------------------------------------------------------
/// errors
/// -----------------------------------------------------------------------
Expand All @@ -25,14 +25,14 @@ contract OptimisticWithdrawalRecipientV2Factory {
/// events
/// -----------------------------------------------------------------------

/// Emitted after a new OptimisticWithdrawalRecipientV2 module is deployed
/// @param owr Address of newly created OptimisticWithdrawalRecipientV2 instance
/// @param owner Owner of newly created OptimisticWithdrawalRecipientV2 instance
/// Emitted after a new ObolValidatorManager instance is deployed
/// @param owr Address of newly created ObolValidatorManager instance
/// @param owner Owner of newly created ObolValidatorManager instance
/// @param recoveryAddress Address to recover non-OWR tokens to
/// @param principalRecipient Address to distribute principal payment to
/// @param rewardRecipient Address to distribute reward payment to
/// @param principalThreshold Principal vs rewards classification threshold (gwei)
event CreateOWRecipient(
event CreateObolValidatorManager(
address indexed owr,
address indexed owner,
address recoveryAddress,
Expand Down Expand Up @@ -91,40 +91,40 @@ contract OptimisticWithdrawalRecipientV2Factory {
/// functions - public & external
/// -----------------------------------------------------------------------

/// Create a new OptimisticWithdrawalRecipientV2 instance
/// Create a new ObolValidatorManager instance
/// @param recoveryAddress Address to receive ERC20 tokens transferred to this contract.
/// If this address is 0x0, ERC20 tokens owned by this contract can be permissionlessly
/// transferred to *either* the principal or reward recipient addresses. If this address is set,
/// ERC20 tokens can only be pushed to the recoveryAddress set.
/// @param owner Owner of the new OptimisticWithdrawalRecipientV2 instance
/// @param owner Owner of the new ObolValidatorManager instance
/// @param principalRecipient Address to distribute principal payments to
/// @param rewardRecipient Address to distribute reward payments to
/// @param recoveryAddress Address to recover non-OWR tokens to
/// @param principalThreshold Principal vs rewards classification threshold (gwei)
/// @return owr Address of the new OptimisticWithdrawalRecipientV2 instance
function createOWRecipient(
/// @return owr Address of the new ObolValidatorManager instance
function createObolValidatorManager(
address owner,
address principalRecipient,
address rewardRecipient,
address recoveryAddress,
uint64 principalThreshold
) external returns (OptimisticWithdrawalRecipientV2 owr) {
) external returns (ObolValidatorManager owr) {
if (principalRecipient == address(0) || rewardRecipient == address(0)) revert Invalid__Recipients();
if (principalThreshold == 0) revert Invalid__ZeroThreshold();
if (principalThreshold > 2048 * 1e9) revert Invalid__ThresholdTooLarge();

owr = new OptimisticWithdrawalRecipientV2(
owr = new ObolValidatorManager(
consolidationSystemContract,
withdrawalSystemContract,
depositSystemContract,
owner,
principalRecipient,
rewardRecipient,
recoveryAddress,
principalThreshold
);
owr.initialize(owner);

emit CreateOWRecipient(
emit CreateObolValidatorManager(
address(owr),
owner,
recoveryAddress,
Expand Down
Loading