8000 PDG: Delegated deposits by failingtwice · Pull Request #1123 · lidofinance/core · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

PDG: Delegated deposits #1123

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

Open
wants to merge 6 commits into
base: feat/vaults
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
21 changes: 21 additions & 0 deletions contracts/0.8.25/vaults/predeposit_guarantee/MeIfNobodyElse.sol
8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
pragma solidity 0.8.25;

/// @title MeIfNobodyElse
/// @author Lido
/// @notice A library for mapping(address => address) that defaults to the key if the value is not set
library MeIfNobodyElse {
/// @notice Returns the value for the key if it is set, otherwise returns the key
function get(mapping(address => address) storage map, address key) internal view returns (address) {
address value = map[key];
return value == address(0) ? key : value;
}

/// @notice Sets the value for the key if it is not the key itself, otherwise resets the value to the zero address
function set(mapping(address => address) storage map, address key, address value) internal {
map[key] = key == value ? address(0) : value;
}
}
if (msg.value != 0) {
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {BLS12_381} from "contracts/0.8.25/lib/BLS.sol";
import {PausableUntilWithRoles} from "contracts/0.8.25/utils/PausableUntilWithRoles.sol";

import {CLProofVerifier} from "./CLProofVerifier.sol";
import {MeIfNobodyElse} from "./MeIfNobodyElse.sol";

import {IStakingVault} from "../interfaces/IStakingVault.sol";
import {IPredepositGuarantee} from "../interfaces/IPredepositGuarantee.sol";
Expand All @@ -31,6 +32,8 @@ import {IPredepositGuarantee} from "../interfaces/IPredepositGuarantee.sol";
* Internal guards for NO<->Guarantor are used only to prevent mistakes and provide operational recovery paths.
* But can not be used to fully prevent misbehavior in this relationship where NO's can access guarantor provided ether.
*
* !NB:
* There is a mutual trust assumption between NO's and the assigned depositor.
*
* !NB:
* PDG is permissionless by design. Anyone can be an NO, provided there is a compatible staking vault
Expand All @@ -40,6 +43,8 @@ import {IPredepositGuarantee} from "../interfaces/IPredepositGuarantee.sol";
* - PDG can be used outside of Lido
*/
contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableUntilWithRoles {
using MeIfNobodyElse for mapping(address => address);

/**
* @notice represents validator stages in PDG flow
* @param NONE - initial stage
Expand Down Expand Up @@ -93,6 +98,7 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
mapping(address nodeOperator => address guarantor) nodeOperatorGuarantor;
mapping(address guarantor => uint256 claimableEther) guarantorClaimableEther;
mapping(bytes validatorPubkey => ValidatorStatus validatorStatus) validatorStatus;
mapping(address nodeOperator => address depositor) nodeOperatorDepositor;
}

uint8 public constant MIN_SUPPORTED_WC_VERSION = 0x01;
Expand Down Expand Up @@ -175,6 +181,15 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
return _guarantorOf(_nodeOperator);
}

/**
* @notice returns address of the depositor for the NO
* @param _nodeOperator to check depositor for
* @return address of depositor for the NO
*/
function nodeOperatorDepositor(address _nodeOperator) external view returns (address) {
return _depositorOf(_nodeOperator);
}

/**
* @notice returns amount of ether refund that guarantor can claim
* @param _guarantor address of the guarantor
Expand Down Expand Up @@ -281,11 +296,25 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
emit GuarantorRefundAdded(prevGuarantor, msg.sender, refund);
}

$.nodeOperatorGuarantor[msg.sender] = _newGuarantor != msg.sender ? _newGuarantor : address(0);
$.nodeOperatorGuarantor.set(msg.sender, _newGuarantor);

emit GuarantorSet(msg.sender, _newGuarantor, prevGuarantor);
}

/**
* @notice sets the depositor for the NO
* @param _newDepositor address of the depositor
*/
function setNodeOperatorDepositor(address _newDepositor) external {
if (_newDepositor == address(0)) revert ZeroArgument("_newDepositor");
address prevDepositor = _depositorOf(msg.sender);
if (_newDepositor == prevDepositor) revert SameDepositor();

_getStorage().nodeOperatorDepositor.set(msg.sender, _newDepositor);

emit DepositorSet(msg.sender, _newDepositor, prevDepositor);
}

/**
* @notice claims refund for the previous guarantor of the NO
* @param _recipient address to send the refund to
Expand Down Expand Up @@ -325,7 +354,7 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
if (_deposits.length == 0) revert EmptyDeposits();

address nodeOperator = _stakingVault.nodeOperator();
if (msg.sender != nodeOperator) revert NotNodeOperator();
if (msg.sender != _depositorOf(nodeOperator)) revert NotDepositor();

// check that node operator is self-guarantor is inside
Expand Down Expand Up @@ -406,8 +435,8 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
IStakingVault _stakingVault,
IStakingVault.Deposit[] calldata _deposits
) public payable whenResumed {
if (msg.sender != _stakingVault.nodeOperator()) {
revert NotNodeOperator();
if (msg.sender != _depositorOf(_stakingVault.nodeOperator())) {
revert NotDepositor();
}
ERC7201Storage storage $ = _getStorage();

Expand All @@ -421,8 +450,8 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
}

// sanity check because first check relies on external contract
if (validator.nodeOperator != msg.sender) {
revert NotNodeOperator();
if (_depositorOf(validator.nodeOperator) != msg.sender) {
revert NotDepositor();
}

if (validator.stakingVault != _stakingVault) {
Expand Down Expand Up @@ -612,8 +641,7 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
/// @notice returns guarantor of the NO
/// @dev if guarantor is not set, returns NO address
function _guarantorOf(address _nodeOperator) internal view returns (address) {
address stored = _getStorage().nodeOperatorGuarantor[_nodeOperator];
return stored == address(0) ? _nodeOperator : stored;
return _getStorage().nodeOperatorGuarantor.get(_nodeOperator);
}

/// @notice enforces that only NO's guarantor can call the function
Expand All @@ -624,6 +652,12 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
_;
}

/// @notice returns depositor of the NO
/// @dev if depositor is not set, returns NO address
function _depositorOf(address _nodeOperator) internal view returns (address) {
return _getStorage().nodeOperatorDepositor.get(_nodeOperator);
}

/// @notice validates that WC belong to the vault
function _validateWC(IStakingVault _stakingVault, bytes32 _withdrawalCredentials) internal pure {
uint8 version = uint8(_withdrawalCredentials[0]);
Expand Down Expand Up @@ -659,9 +693,11 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
event BalanceCompensated(address indexed nodeOperator, address indexed to, uint128 total, uint128 locked);
event BalanceRefunded(address indexed nodeOperator, address indexed to);

/// NO Guarantor events
/// NO delegate events

event GuarantorSet(address indexed nodeOperator, address indexed newGuarantor, address indexed prevGuarantor);
event DepositorSet(address indexed nodeOperator, address indexed newDepositor, address indexed prevDepositor);

event GuarantorRefundAdded(address indexed guarantor, address indexed nodeOperator, uint256 amount);
event GuarantorRefundClaimed(address indexed guarantor, address indexed recipient, uint256 amount);

Expand Down Expand Up @@ -700,6 +736,7 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
error NothingToRefund();
error WithdrawalFailed();
error SameGuarantor();
error SameDepositor();
error RefundFailed();

// predeposit errors
Expand Down Expand Up @@ -727,7 +764,7 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
// auth
error NotStakingVaultOwner();
error NotGuarantor();
error NotNodeOperator();
error NotDepositor();

// general
error ZeroArgument(string argument);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: UNLICENSED
// for testing purposes only

pragma solidity 0.8.25;

import {IStakingVault} from "contracts/0.8.25/vaults/interfaces/IStakingVault.sol";

contract StakingVault__MockForPDG {
event Mock_depositToBeaconChain(address indexed _depositor, uint256 _depositCount, uint256 _totalDepositAmount);

uint256 private constant WC_0X02_PREFIX = 0x02 << 248;

address private nodeOperator_;
address private owner_;
bytes32 private withdrawalCredentials_;

constructor(address _owner, address _nodeOperator) {
owner_ = _owner;
nodeOperator_ = _nodeOperator;
}

function totalValue() external view returns (uint256) {
return address(this).balance;
}

function fund() external payable {}

function setNodeOperator(address _nodeOperator) external {
nodeOperator_ = _nodeOperator;
}

function setOwner(address _owner) external {
owner_ = _owner;
}

function withdrawalCredentials() public view returns (bytes32) {
return withdrawalCredentials_ == bytes32(0) ? bytes32(WC_0X02_PREFIX | uint160(address(this))) : withdrawalCredentials_;
}

function nodeOperator() external view returns (address) {
return nodeOperator_;
}

function owner() external view returns (address) {
return owner_;
}

function depositToBeaconChain(IStakingVault.Deposit[] calldata _deposits) external {
uint256 totalDepositAmount = 0;
for (uint256 i = 0; i < _deposits.length; i++) {
totalDepositAmount += _deposits[i].amount;
}

emit Mock_depositToBeaconChain(msg.sender, _deposits.length, totalDepositAmount);
}

function mock__setWithdrawalCredentials(bytes32 _withdrawalCredentials) external {
withdrawalCredentials_ = _withdrawalCredentials;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
// for testing purposes only

pragma solidity 0.8.25;



contract VaultHub__MockForPDG {

}
Loading
Loading
0