Skip to content

Commit

Permalink
feat: enhance PredepositGuarantee with events and improved error hand…
Browse files Browse the repository at this point in the history
…ling
  • Loading branch information
DiRaiks committed Feb 6, 2025
1 parent ecc06c6 commit ef47aed
Showing 1 changed file with 72 additions and 45 deletions.
117 changes: 72 additions & 45 deletions contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,40 @@ contract PredepositGuarantee is CLProofVerifier {
address nodeOperator;
}

// Events
event NodeOperatorBondToppedUp(address indexed nodeOperator, uint256 amount);
event NodeOperatorBondWithdrawn(address indexed nodeOperator, uint256 amount, address indexed recipient);
event NodeOperatorVoucherSet(address indexed nodeOperator, address indexed voucher);
event ValidatorPreDeposited(address indexed nodeOperator, address indexed stakingVault, uint256 numberOfDeposits, uint256 totalDepositAmount);
event ValidatorProven(address indexed nodeOperator, bytes indexed validatorPubkey, address indexed stakingVault, bytes32 withdrawalCredentials);
event ValidatorDisproven(address indexed nodeOperator, bytes indexed validatorPubkey, address indexed stakingVault, bytes32 withdrawalCredentials);
event ValidatorDisprovenWithdrawn(address indexed nodeOperator, bytes indexed validatorPubkey, address indexed stakingVault, address recipient);

constructor(GIndex _gIFirstValidator) CLProofVerifier(_gIFirstValidator) {}

mapping(address nodeOperator => NodeOperatorBond bond) public nodeOperatorBonds;
mapping(address nodeOperator => address voucher) public nodeOperatorVoucher;

mapping(bytes validatorPubkey => ValidatorStatus validatorStatus) public validatorStatuses;

/// NO Balance operations
// View functions

function topUpNodeOperatorBond(address _nodeOperator) external payable {
if (msg.value == 0) revert ZeroArgument("msg.value");
if (_nodeOperator == address(0)) revert ZeroArgument("_nodeOperator");
function nodeOperatorBond(address _nodeOperator) external view returns (NodeOperatorBond memory) {
return nodeOperatorBonds[_nodeOperator];
}

_validateNodeOperatorCaller(_nodeOperator);
function nodeOperatorVoucherAddress(address _nodeOperator) external view returns (address) {
return nodeOperatorVoucher[_nodeOperator];
}

nodeOperatorBonds[_nodeOperator].total += uint128(msg.value);
function validatorStatus(bytes calldata _validatorPubkey) external view returns (ValidatorStatus memory) {
return validatorStatuses[_validatorPubkey];
}

/// NO Balance operations

function topUpNodeOperatorBond(address _nodeOperator) external payable {
_topUpNodeOperatorCollateral(_nodeOperator, msg.value);
}

function withdrawNodeOperatorBond(address _nodeOperator, uint128 _amount, address _recipient) external {
Expand All @@ -54,13 +72,16 @@ contract PredepositGuarantee is CLProofVerifier {

_validateNodeOperatorCaller(_nodeOperator);

if (nodeOperatorBonds[_nodeOperator].total - nodeOperatorBonds[_nodeOperator].locked < _amount)
revert NotEnoughUnlockedCollateralToWithdraw();
uint256 unlockedCollateral = nodeOperatorBonds[_nodeOperator].total - nodeOperatorBonds[_nodeOperator].locked;

if (unlockedCollateral < _amount)
revert NotEnoughUnlockedCollateralToWithdraw(unlockedCollateral, _amount);

nodeOperatorBonds[_nodeOperator].total -= _amount;
(bool success, ) = _recipient.call{value: uint256(_amount)}("");
if (!success) revert WithdrawalFailed();
// TODO: event

emit NodeOperatorBondWithdrawn(_nodeOperator, _amount, _recipient);
}

function setNodeOperatorVoucher(address _voucher) external {
Expand All @@ -71,16 +92,17 @@ contract PredepositGuarantee is CLProofVerifier {
if (bond.locked != 0) revert BondMustBeFullyUnlocked();

if (bond.total > 0 && nodeOperatorVoucher[msg.sender] != address(0)) {
uint256 bondAmount = bond.total;
nodeOperatorBonds[msg.sender].total = 0;
(bool success, ) = nodeOperatorVoucher[msg.sender].call{value: uint256(bond.total)}("");
(bool success, ) = nodeOperatorVoucher[msg.sender].call{value: bondAmount}("");

// voucher can block change?
if (!success) revert WithdrawalFailed();
}

nodeOperatorVoucher[msg.sender] = _voucher;

// TODO: event
emit NodeOperatorVoucherSet(msg.sender, _voucher);
}

/// Deposit operations
Expand All @@ -96,23 +118,24 @@ contract PredepositGuarantee is CLProofVerifier {

// optional top up
if (msg.value != 0) {
_topUpNodeOperatorCollateral(_nodeOperator);
_topUpNodeOperatorCollateral(_nodeOperator, msg.value);
}

uint128 totalDepositAmount = PREDEPOSIT_AMOUNT * uint128(_deposits.length);
uint256 unlockedCollateral = nodeOperatorBonds[_nodeOperator].total - nodeOperatorBonds[_nodeOperator].locked;

if (nodeOperatorBonds[_nodeOperator].total - nodeOperatorBonds[_nodeOperator].locked < totalDepositAmount)
revert NotEnoughUnlockedCollateralToPredeposit();
if (unlockedCollateral < totalDepositAmount)
revert NotEnoughUnlockedCollateralToPredeposit(unlockedCollateral, totalDepositAmount);

for (uint256 i = 0; i < _deposits.length; i++) {
IStakingVaultOwnable.Deposit calldata _deposit = _deposits[i];

if (validatorStatuses[_deposit.pubkey].bondStatus != BondStatus.NO_RECORD) {
revert MustBeNewValidatorPubkey();
revert MustBeNewValidatorPubkey(_deposit.pubkey, validatorStatuses[_deposit.pubkey].bondStatus);
}

// cannot predeposit a validator with a deposit amount that is not 1 ether
if (_deposit.amount != PREDEPOSIT_AMOUNT) revert PredepositDepositAmountInvalid();
if (_deposit.amount != PREDEPOSIT_AMOUNT) revert PredepositDepositAmountInvalid(_deposit.pubkey, _deposit.amount);

validatorStatuses[_deposit.pubkey] = ValidatorStatus({
bondStatus: BondStatus.AWAITING_PROOF,
Expand All @@ -123,7 +146,8 @@ contract PredepositGuarantee is CLProofVerifier {

nodeOperatorBonds[_nodeOperator].locked += totalDepositAmount;
_stakingVault.depositToBeaconChain(_deposits);
// TODO: event

emit ValidatorPreDeposited(_nodeOperator, address(_stakingVault), _deposits.length, totalDepositAmount);
}

function proveValidatorWC(ValidatorWitness calldata _witness) external {
Expand All @@ -133,7 +157,7 @@ contract PredepositGuarantee is CLProofVerifier {
function depositToProvenValidators(
IStakingVaultOwnable _stakingVault,
IStakingVaultOwnable.Deposit[] calldata _deposits
) public payable {
) public {
if (msg.sender != _stakingVault.nodeOperator()) {
revert MustBeNodeOperator();
}
Expand All @@ -142,11 +166,11 @@ contract PredepositGuarantee is CLProofVerifier {
IStakingVaultOwnable.Deposit calldata _deposit = _deposits[i];

if (validatorStatuses[_deposit.pubkey].bondStatus != BondStatus.PROVED) {
revert DepositToUnprovenValidator();
revert DepositToUnprovenValidator(_deposit.pubkey, validatorStatuses[_deposit.pubkey].bondStatus);
}

if (validatorStatuses[_deposit.pubkey].stakingVault != _stakingVault) {
revert DepositToWrongVault();
revert DepositToWrongVault(_deposit.pubkey, address(_stakingVault));
}
}

Expand Down Expand Up @@ -182,15 +206,15 @@ contract PredepositGuarantee is CLProofVerifier {

if (msg.sender != validatorStatus.stakingVault.owner()) revert WithdrawSenderNotStakingVaultOwner();

if (validatorStatus.bondStatus != BondStatus.PROVED_INVALID) revert ValidatorNotProvenInvalid();
if (validatorStatus.bondStatus != BondStatus.PROVED_INVALID) revert ValidatorNotProvenInvalid(validatorStatus.bondStatus);

validatorStatus.bondStatus = BondStatus.WITHDRAWN;

(bool success, ) = _recipient.call{value: PREDEPOSIT_AMOUNT}("");

if (!success) revert WithdrawalFailed();

//TODO: events
emit ValidatorDisprovenWithdrawn(validatorStatus.nodeOperator, validatorPubkey, address(validatorStatus.stakingVault), _recipient);
}

/// Internal functions
Expand All @@ -201,8 +225,15 @@ contract PredepositGuarantee is CLProofVerifier {
revert MustBeNodeOperatorOrVoucher();
}

function _topUpNodeOperatorCollateral(address _nodeOperator) internal {
// TODO: event
function _topUpNodeOperatorCollateral(address _nodeOperator, uint256 _amount) internal {
if (_amount == 0) revert ZeroArgument("msg.value");
if (_nodeOperator == address(0)) revert ZeroArgument("_nodeOperator");

_validateNodeOperatorCaller(_nodeOperator);

nodeOperatorBonds[_nodeOperator].total += uint128(_amount);

emit NodeOperatorBondToppedUp(_nodeOperator, _amount);
}

function _wcToAddress(bytes32 _withdrawalCredentials) internal pure returns (address) {
Expand All @@ -217,13 +248,13 @@ contract PredepositGuarantee is CLProofVerifier {
ValidatorStatus storage validatorStatus = validatorStatuses[_witness.validator.pubkey];

if (validatorStatus.bondStatus != BondStatus.AWAITING_PROOF) {
revert ValidatorNotPreDeposited();
revert ValidatorNotPreDeposited(_witness.validator.pubkey, validatorStatus.bondStatus);
}

(uint64 _wcVersion, address _wcAddress) = _deconstructWC(_witness.validator.withdrawalCredentials);

if (_wcVersion < 1) {
revert WithdrawalCredentialsAreInvalid();
revert WithdrawalCredentialsAreInvalid(_witness.validator.pubkey);
}

_validateWCProof(_witness);
Expand All @@ -232,15 +263,17 @@ contract PredepositGuarantee is CLProofVerifier {
if (address(validatorStatus.stakingVault) == _wcAddress) {
// stricter WC check to ensure WC version matches
if (validatorStatus.stakingVault.withdrawalCredentials() != _witness.validator.withdrawalCredentials) {
revert WithdrawalCredentialsAreInvalid();
revert WithdrawalCredentialsAreInvalid(_witness.validator.pubkey);
}

validatorStatus.bondStatus = BondStatus.PROVED;
// TODO: positive events

emit ValidatorProven(validatorStatus.nodeOperator, _witness.validator.pubkey, address(validatorStatus.stakingVault), _witness.validator.withdrawalCredentials);
} else {
validatorStatus.bondStatus = BondStatus.PROVED_INVALID;
nodeOperatorBonds[validatorStatus.nodeOperator].total -= PREDEPOSIT_AMOUNT;
// TODO: negative events

emit ValidatorDisproven(validatorStatus.nodeOperator, _witness.validator.pubkey, address(validatorStatus.stakingVault), _witness.validator.withdrawalCredentials);
}
nodeOperatorBonds[validatorStatus.nodeOperator].locked -= PREDEPOSIT_AMOUNT;
}
Expand All @@ -251,32 +284,26 @@ contract PredepositGuarantee is CLProofVerifier {

// predeposit errors
error PredepositNoDeposits();
error PredepositValueNotMultipleOfPrediposit();
error PredepositDepositAmountInvalid();
error MustBeNewValidatorPubkey();
error NotEnoughUnlockedCollateralToPredeposit();
error PredepositDepositAmountInvalid(bytes validatorPubkey, uint256 depositAmount);
error MustBeNewValidatorPubkey(bytes validatorPubkey, BondStatus bondStatus);
error NotEnoughUnlockedCollateralToPredeposit(uint256 unlockedCollateral, uint256 totalDepositAmount);

// depositing errors
error DepositToUnprovenValidator();
error DepositToWrongVault();
error ValidatorNotPreDeposited();
error DepositToUnprovenValidator(bytes validatorPubkey, BondStatus bondStatus);
error DepositToWrongVault(bytes validatorPubkey, address stakingVault);
error ValidatorNotPreDeposited(bytes validatorPubkey, BondStatus bondStatus);

// prove
error WithdrawalCredentialsAreInvalid();

error WithdrawalCredentialsAreInvalid(bytes validatorPubkey);
// withdrawal proven
error NotEnoughUnlockedCollateralToWithdraw();
error NotEnoughUnlockedCollateralToWithdraw(uint256 unlockedCollateral, uint256 amount);

// withdrawal disproven
error ValidatorNotProvenInvalid();
error ValidatorNotProvenInvalid(BondStatus bondStatus);
error WithdrawSenderNotStakingVaultOwner();
error WithdrawSenderNotNodeOperator();
error WithdrawValidatorDoesNotBelongToNodeOperator();
error WithdrawalCollateralOfWrongVault();
error WithdrawalCredentialsAreValid();
error WithdrawToVaultNotAllowed();
/// withdrawal generic
error WithdrawalFailed();
error WithdrawToVaultNotAllowed();

// auth
error MustBeNodeOperatorOrVoucher();
Expand Down

0 comments on commit ef47aed

Please sign in to comment.