Skip to content

Commit

Permalink
Merge pull request #175 from yieldnest/feature/WithdrawalsProcessor-w…
Browse files Browse the repository at this point in the history
…rapper

add src/WithdrawalsProcessor.sol convenience to bundle processing and…
  • Loading branch information
danoctavian authored Oct 7, 2024
2 parents 87ec4ad + 9ea8df0 commit 5133f73
Show file tree
Hide file tree
Showing 7 changed files with 569 additions and 27 deletions.
228 changes: 228 additions & 0 deletions broadcast/DeployYnETHWithdrawals.s.sol/17000/run-1728146182.json

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions deployments/ynETHWithdrawalsProcessor-17000.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"DEPLOYER": "0xd7d4A7f14265B0A69AE482b85B0a8496c3F99fb9",
"implementation-stakingNodesManager": "0x0000000000000000000000000000000000000000",
"implementation-withdrawalQueueManager": "0x0000000000000000000000000000000000000000",
"implementation-withdrawalsProcessor": "0x36a38AA91947DbE6539e19512E6FF26576015Bb2",
"implementation-ynETH": "0x0000000000000000000000000000000000000000",
"implementation-ynETHRedemptionAssetsVault": "0x0000000000000000000000000000000000000000",
"proxy-withdrawalQueueManager": "0x0000000000000000000000000000000000000000",
"proxy-withdrawalsProcessor": "0x48E3FdCE3E2d5A3Fa34bdEd9eb9dEeBB48217ba3",
"proxy-ynETHRedemptionAssetsVault": "0x0000000000000000000000000000000000000000",
"proxyAdmin-withdrawalQueueManager": "0x0000000000000000000000000000000000000000",
"proxyAdmin-withdrawalsProcessor": "0x22D9bc7555875af63a51c20026f02E752781A8c0",
"proxyAdmin-ynETHRedemptionAssetsVault": "0x0000000000000000000000000000000000000000",
"stakingNodeImplementation": "0x0000000000000000000000000000000000000000"
}
7 changes: 5 additions & 2 deletions script/ContractAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract ContractAddresses {
address CONSENSUS_LAYER_RECEIVER_ADDRESS;
address YNETH_REDEMPTION_ASSETS_VAULT_ADDRESS;
address WITHDRAWAL_QUEUE_MANAGER_ADDRESS;
address WITHDRAWALS_PROCESSOR_ADDRESS;
}

struct EigenlayerAddresses {
Expand Down Expand Up @@ -123,7 +124,8 @@ contract ContractAddresses {
EXECUTION_LAYER_RECEIVER_ADDRESS: 0x1D6b2a11FFEa5F9a8Ed85A02581910b3d695C12b,
CONSENSUS_LAYER_RECEIVER_ADDRESS: 0xE439fe4563F7666FCd7405BEC24aE7B0d226536e,
YNETH_REDEMPTION_ASSETS_VAULT_ADDRESS: address(0),
WITHDRAWAL_QUEUE_MANAGER_ADDRESS: address(0)
WITHDRAWAL_QUEUE_MANAGER_ADDRESS: address(0),
WITHDRAWALS_PROCESSOR_ADDRESS: address(0)
}),
ynEigen: YnEigenAddresses({
YNEIGEN_ADDRESS: 0x35Ec69A77B79c255e5d47D5A3BdbEFEfE342630c,
Expand Down Expand Up @@ -180,7 +182,8 @@ contract ContractAddresses {
EXECUTION_LAYER_RECEIVER_ADDRESS: 0xA5E9E1ceb4cC1854d0e186a9B3E67158b84AD072,
CONSENSUS_LAYER_RECEIVER_ADDRESS: 0x706EED02702fFE9CBefD6A65E63f3C2b59B7eF2d,
YNETH_REDEMPTION_ASSETS_VAULT_ADDRESS: 0x3a2DD2f0f5A20768110a52fC4f091AB9d8631b58,
WITHDRAWAL_QUEUE_MANAGER_ADDRESS: 0x141aAb320857145fB42240C979b800f48CE5B678
WITHDRAWAL_QUEUE_MANAGER_ADDRESS: 0x141aAb320857145fB42240C979b800f48CE5B678,
WITHDRAWALS_PROCESSOR_ADDRESS: 0x48E3FdCE3E2d5A3Fa34bdEd9eb9dEeBB48217ba3
}),
ynEigen: YnEigenAddresses({
YNEIGEN_ADDRESS: 0x071bdC8eDcdD66730f45a3D3A6F794FAA37C75ED,
Expand Down
59 changes: 36 additions & 23 deletions script/DeployYnETHWithdrawals.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {ynETHRedemptionAssetsVault} from "src/ynETHRedemptionAssetsVault.sol";
import {WithdrawalQueueManager} from "src/WithdrawalQueueManager.sol";
import {IRedemptionAssetsVault} from "src/interfaces/IRedemptionAssetsVault.sol";
import {IRedeemableAsset} from "src/interfaces/IRedeemableAsset.sol";

import {WithdrawalsProcessor} from "src/WithdrawalsProcessor.sol";

import {console} from "lib/forge-std/src/console.sol";

Expand All @@ -36,6 +36,7 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {
struct WithdrawalsDeployment {
ynETHRedemptionAssetsVault ynETHRedemptionAssetsVault;
WithdrawalQueueManager withdrawalQueueManager;
WithdrawalsProcessor withdrawalsProcessor;
StakingNodesManager stakingNodesManagerImplementation;
StakingNode stakingNodeImplementation;
ynETH ynETHImplementation;
Expand All @@ -51,6 +52,10 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {

ynETHRedemptionAssetsVault public ynETHRedemptionAssetsVaultInstance;
WithdrawalQueueManager public ynETHWithdrawalQueueManager;
WithdrawalsProcessor withdrawalsProcessor;
StakingNodesManager stakingNodesManagerImplementation;
StakingNode stakingNodeImplementation;
ynETH ynETHImplementation;
ActorAddresses.Actors actors;
address deployer;

Expand All @@ -61,29 +66,15 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {

ContractAddresses contractAddresses = new ContractAddresses();

if (false) {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

address publicKey = vm.addr(deployerPrivateKey);
console.log("Deployer Public Key:", publicKey);
deployer = publicKey;

IynETH yneth = IynETH(payable(contractAddresses.getChainAddresses(block.chainid).yn.YNETH_ADDRESS));

// // Apply preprod overrides for DEFAULT_SIGNER
// if (block.chainid == 17000 && vm.envBool("PREPROD")) { // Holesky testnet or PREPROD environment
// address DEFAULT_SIGNER = actors.eoa.DEFAULT_SIGNER;
// actors.ops.WITHDRAWAL_MANAGER = DEFAULT_SIGNER;
// actors.ops.REDEMPTION_ASSET_WITHDRAWER = DEFAULT_SIGNER;
// actors.ops.REQUEST_FINALIZER = DEFAULT_SIGNER;
// actors.ops.STAKING_NODES_WITHDRAWER = DEFAULT_SIGNER;
// actors.admin.FEE_RECEIVER = DEFAULT_SIGNER;
// actors.admin.ADMIN = DEFAULT_SIGNER;
// // Override ynETH address for Holesky testnet
// yneth = IynETH(payable(0xe8A0fA11735b9C91F5F89340A2E2720e9c9d19fb));
// console.log("Overridden ynETH address:", address(yneth));
// console.log("Applied preprod overrides for Holesky testnet");
// }
// Get the StakingNodesManager instance
IStakingNodesManager stakingNodesManager = IStakingNodesManager(contractAddresses.getChainAddresses(block.chainid).yn.STAKING_NODES_MANAGER_ADDRESS);

address _broadcaster = vm.addr(deployerPrivateKey);

Expand All @@ -95,13 +86,13 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {


// Deploy implementation contracts
StakingNodesManager stakingNodesManagerImplementation = new StakingNodesManager();
stakingNodesManagerImplementation = new StakingNodesManager();
console.log("StakingNodesManager implementation deployed at:", address(stakingNodesManagerImplementation));

StakingNode stakingNodeImplementation = new StakingNode();
console.log("StakingNode implementation deployed at:", address(stakingNodeImplementation));

ynETH ynETHImplementation = new ynETH();
ynETHImplementation = new ynETH();
console.log("ynETH implementation deployed at:", address(ynETHImplementation));

// deploy ynETHRedemptionAssetsVault
Expand All @@ -124,6 +115,17 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {
ynETHWithdrawalQueueManager = WithdrawalQueueManager(address(_proxy));
}

// deploy WithdrawalsProcessor
WithdrawalsProcessor withdrawalsProcessorImplementation = new WithdrawalsProcessor();
console.log("WithdrawalsProcessor implementation deployed at:", address(withdrawalsProcessorImplementation));

TransparentUpgradeableProxy withdrawalsProcessorProxy = new TransparentUpgradeableProxy(
address(withdrawalsProcessorImplementation),
actors.admin.PROXY_ADMIN_OWNER,
""
);
withdrawalsProcessor = WithdrawalsProcessor(address(withdrawalsProcessorProxy));

// initialize ynETHRedemptionAssetsVault
{
ynETHRedemptionAssetsVault.Init memory _init = ynETHRedemptionAssetsVault.Init({
Expand Down Expand Up @@ -151,6 +153,17 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {
ynETHWithdrawalQueueManager.initialize(managerInit);
}

{
// initialize WithdrawalsProcessor
withdrawalsProcessor.initialize(
IStakingNodesManager(address(stakingNodesManager)),
actors.admin.ADMIN,
actors.ops.WITHDRAWAL_MANAGER
);
console.log("WithdrawalsProcessor initialized");

}

// Verify all the above is deployed correctly.

// Perform the following permissioned call with DEFAULT_ADMIN ROLE:
Expand All @@ -160,20 +173,19 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {
console.log("withdrawalManager:", actors.ops.WITHDRAWAL_MANAGER);
console.log("stakingNodesWithdrawer:", actors.ops.STAKING_NODES_WITHDRAWER);


// Save deployment information
WithdrawalsDeployment memory deployment = WithdrawalsDeployment({
ynETHRedemptionAssetsVault: ynETHRedemptionAssetsVaultInstance,
withdrawalQueueManager: ynETHWithdrawalQueueManager,
withdrawalsProcessor: withdrawalsProcessor,
stakingNodesManagerImplementation: stakingNodesManagerImplementation,
stakingNodeImplementation: stakingNodeImplementation,
ynETHImplementation: ynETHImplementation
});

saveDeployment(deployment);
saveWithdrawalsDeployment(deployment);

console.log("Deployment information saved successfully.");
}

ynETHRedemptionAssetsVaultInstance = ynETHRedemptionAssetsVault(payable(0x3a2DD2f0f5A20768110a52fC4f091AB9d8631b58));
// initialize stakingNodesManager withdrawal contracts
Expand Down Expand Up @@ -201,12 +213,13 @@ contract DeployYnETHWithdrawals is BaseYnETHScript {
return string.concat(root, "/deployments/ynETHWithdrawals-", vm.toString(block.chainid), ".json");
}

function saveDeployment(WithdrawalsDeployment memory deployment) public virtual {
function saveWithdrawalsDeployment(WithdrawalsDeployment memory deployment) public virtual {
string memory json = "deployment";

// contract addresses
serializeProxyElements(json, "withdrawalQueueManager", address(deployment.withdrawalQueueManager));
serializeProxyElements(json, "ynETHRedemptionAssetsVault", address(deployment.ynETHRedemptionAssetsVault));
serializeProxyElements(json, "withdrawalsProcessor", address(deployment.withdrawalsProcessor));
vm.serializeAddress(json, "stakingNodeImplementation", address(deployment.stakingNodeImplementation));
vm.serializeAddress(json, "implementation-stakingNodesManager", address(deployment.stakingNodesManagerImplementation));
vm.serializeAddress(json, "implementation-ynETH", address(deployment.ynETHImplementation));
Expand Down
102 changes: 102 additions & 0 deletions src/WithdrawalsProcessor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: BSD 3-Clause License
pragma solidity ^0.8.24;

import {IStakingNodesManager} from "./interfaces/IStakingNodesManager.sol";
import {IDelegationManager} from "lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStakingNodesManager, IStakingNodesManager as WithdrawalAction} from "./interfaces/IStakingNodesManager.sol";
import {Initializable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {AccessControlUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol";

interface IWithdrawalsProcessorEvents {
event WithdrawalsCompletedAndProcessed(
IStakingNodesManager.WithdrawalAction withdrawalAction,
uint256 withdrawalsCount
);
}

contract WithdrawalsProcessor is Initializable, AccessControlUpgradeable, IWithdrawalsProcessorEvents {

//--------------------------------------------------------------------------------------
//---------------------------------- ERRORS ------------------------------------------
//--------------------------------------------------------------------------------------

error ZeroAddress();

//--------------------------------------------------------------------------------------
//---------------------------------- ROLES -------------------------------------------
//--------------------------------------------------------------------------------------

bytes32 public constant WITHDRAWAL_MANAGER_ROLE = keccak256("WITHDRAWAL_MANAGER_ROLE");

//--------------------------------------------------------------------------------------
//---------------------------------- VARIABLES ---------------------------------------
//--------------------------------------------------------------------------------------

IStakingNodesManager public stakingNodesManager;

//--------------------------------------------------------------------------------------
//---------------------------------- INITIALIZATION ----------------------------------
//--------------------------------------------------------------------------------------


/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(
IStakingNodesManager _stakingNodesManager,
address _admin,
address _withdrawalManager
) public initializer
notZeroAddress(address(_stakingNodesManager))
notZeroAddress(_withdrawalManager)
{
__AccessControl_init();

stakingNodesManager = _stakingNodesManager;
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
_grantRole(WITHDRAWAL_MANAGER_ROLE, _withdrawalManager);
}

/**
* @notice Bundles the completion of queued withdrawals and processing of principal withdrawals for a single node
* @param withdrawalAction The withdrawal action containing node ID and withdrawal amounts
* @param withdrawals Array of withdrawals to complete
* @param middlewareTimesIndexes Array of middleware times indexes for the withdrawals
*/
function completeAndProcessWithdrawalsForNode(
IStakingNodesManager.WithdrawalAction memory withdrawalAction,
IDelegationManager.Withdrawal[] memory withdrawals,
uint256[] memory middlewareTimesIndexes
) external onlyRole(WITHDRAWAL_MANAGER_ROLE) {
// Complete queued withdrawals
stakingNodesManager.nodes(withdrawalAction.nodeId).completeQueuedWithdrawals(withdrawals, middlewareTimesIndexes);

// Process principal withdrawal
IStakingNodesManager.WithdrawalAction[] memory actions = new IStakingNodesManager.WithdrawalAction[](1);
actions[0] = withdrawalAction;
stakingNodesManager.processPrincipalWithdrawals(actions);

// Emit an event for the completed and processed withdrawals
emit WithdrawalsCompletedAndProcessed(
withdrawalAction,
withdrawals.length
);
}

//--------------------------------------------------------------------------------------
//---------------------------------- MODIFIERS ---------------------------------------
//--------------------------------------------------------------------------------------

/**
* @notice Ensure that the given address is not the zero address.
* @param _address The address to check.
*/
modifier notZeroAddress(address _address) {
if (_address == address(0)) {
revert ZeroAddress();
}
_;
}
}
36 changes: 35 additions & 1 deletion test/integration/M3/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {WithdrawalQueueManager} from "../../../src/WithdrawalQueueManager.sol";
import {ynETHRedemptionAssetsVault} from "../../../src/ynETHRedemptionAssetsVault.sol";
import {IStakingNode} from "../../../src/interfaces/IStakingNodesManager.sol";
import {PlaceholderStakingNodesManager} from "./PlaceholderStakingNodesManager.sol";
import {WithdrawalsProcessor} from "src/WithdrawalsProcessor.sol";



Expand Down Expand Up @@ -61,6 +62,7 @@ contract Base is Test, Utils {
// Withdrawals
WithdrawalQueueManager public ynETHWithdrawalQueueManager;
ynETHRedemptionAssetsVault public ynETHRedemptionAssetsVaultInstance;
WithdrawalsProcessor public withdrawalsProcessor;

// EigenLayer
IEigenPodManager public eigenPodManager;
Expand Down Expand Up @@ -115,8 +117,9 @@ contract Base is Test, Utils {
}

function upgradeYnToM3() internal {
if (block.chainid != 1) {
if (block.chainid == 17000) {
// Nothing to do, only mainnet left to upgrade
deployWithdrawalsProcessor();
return;
}

Expand Down Expand Up @@ -286,9 +289,40 @@ contract Base is Test, Utils {
vm.stopPrank();
}

deployWithdrawalsProcessor();

runUpgradeIntegrityInvariants(preUpgradeState);
}

function deployWithdrawalsProcessor() internal {
// Deploy WithdrawalsProcessor
// Deploy the implementation contract
WithdrawalsProcessor withdrawalsProcessorImplementation = new WithdrawalsProcessor();

// Prepare the initialization data
bytes memory initData = abi.encodeWithSelector(
WithdrawalsProcessor.initialize.selector,
IStakingNodesManager(address(stakingNodesManager)),
actors.admin.ADMIN,
actors.ops.WITHDRAWAL_MANAGER
);

// Deploy the proxy
TransparentUpgradeableProxy withdrawalsProcessorProxy = new TransparentUpgradeableProxy(
address(withdrawalsProcessorImplementation),
actors.admin.PROXY_ADMIN_OWNER,
initData
);

withdrawalsProcessor = WithdrawalsProcessor(address(withdrawalsProcessorProxy));

// Grant WITHDRAWAL_MANAGER_ROLE to WithdrawalsProcessor
vm.startPrank(actors.admin.ADMIN);
stakingNodesManager.grantRole(stakingNodesManager.WITHDRAWAL_MANAGER_ROLE(), address(withdrawalsProcessor));
stakingNodesManager.grantRole(stakingNodesManager.STAKING_NODES_WITHDRAWER_ROLE(), address(withdrawalsProcessor));
vm.stopPrank();
}

function createValidators(uint256[] memory nodeIds, uint256 count) public returns (uint40[] memory) {
uint40[] memory validatorIndices = new uint40[](count * nodeIds.length);
uint256 index = 0;
Expand Down
Loading

0 comments on commit 5133f73

Please sign in to comment.