diff --git a/contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol b/contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol index 29c0b2055..e41f60765 100644 --- a/contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol +++ b/contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol @@ -30,6 +30,15 @@ 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; @@ -37,15 +46,24 @@ contract PredepositGuarantee is CLProofVerifier { 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 { @@ -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 { @@ -71,8 +92,9 @@ 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(); @@ -80,7 +102,7 @@ contract PredepositGuarantee is CLProofVerifier { nodeOperatorVoucher[msg.sender] = _voucher; - // TODO: event + emit NodeOperatorVoucherSet(msg.sender, _voucher); } /// Deposit operations @@ -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, @@ -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 { @@ -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(); } @@ -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)); } } @@ -182,7 +206,7 @@ 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; @@ -190,7 +214,7 @@ contract PredepositGuarantee is CLProofVerifier { if (!success) revert WithdrawalFailed(); - //TODO: events + emit ValidatorDisprovenWithdrawn(validatorStatus.nodeOperator, validatorPubkey, address(validatorStatus.stakingVault), _recipient); } /// Internal functions @@ -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) { @@ -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); @@ -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; } @@ -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();