From ff311eb91ca77927fa290ee1f02d018e53129578 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Wed, 16 Oct 2024 10:40:21 +0200 Subject: [PATCH] feat(contracts): indicate minimum collateral requirements in manager view methods --- bolt-contracts/src/contracts/BoltManager.sol | 17 +++++++++++++++-- bolt-contracts/src/interfaces/IBoltManager.sol | 9 +++++++++ .../src/interfaces/IBoltValidators.sol | 7 +++++++ .../test/BoltManager.EigenLayer.t.sol | 2 +- bolt-contracts/test/BoltManager.Symbiotic.t.sol | 2 +- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/bolt-contracts/src/contracts/BoltManager.sol b/bolt-contracts/src/contracts/BoltManager.sol index f2c9e50d7..de8a8461e 100644 --- a/bolt-contracts/src/contracts/BoltManager.sol +++ b/bolt-contracts/src/contracts/BoltManager.sol @@ -130,7 +130,7 @@ contract BoltManager is IBoltManager, OwnableUpgradeable, UUPSUpgradeable { /// @notice Get the status of multiple proposers, given their pubkey hashes. /// @param pubkeyHashes The pubkey hashes of the proposers to get the status for. /// @return statuses The statuses of the proposers, including their operator and active stake. - function getProposersStatus( + function getProposerStatuses( bytes32[] calldata pubkeyHashes ) public view returns (IBoltValidators.ProposerStatus[] memory statuses) { statuses = new IBoltValidators.ProposerStatus[](pubkeyHashes.length); @@ -150,12 +150,12 @@ contract BoltManager is IBoltManager, OwnableUpgradeable, UUPSUpgradeable { } uint48 epochStartTs = getEpochStartTs(getEpochAtTs(Time.timestamp())); + // NOTE: this will revert when the proposer does not exist. IBoltValidators.Validator memory validator = validators.getValidatorByPubkeyHash(pubkeyHash); Operator memory operator = operators.get(validator.authorizedOperator); status.pubkeyHash = pubkeyHash; - status.active = validator.exists; status.operator = validator.authorizedOperator; status.operatorRPC = operator.rpc; @@ -167,6 +167,19 @@ contract BoltManager is IBoltManager, OwnableUpgradeable, UUPSUpgradeable { (status.collaterals, status.amounts) = IBoltMiddleware(operator.middleware).getOperatorCollaterals(validator.authorizedOperator); + // NOTE: check if the sum of the collaterals covers the minimum operator stake required. + + uint256 totalOperatorStake = 0; + for (uint256 i = 0; i < status.amounts.length; ++i) { + totalOperatorStake += status.amounts[i]; + } + + if (totalOperatorStake < parameters.MINIMUM_OPERATOR_STAKE()) { + status.active = false; + } else { + status.active = true; + } + return status; } diff --git a/bolt-contracts/src/interfaces/IBoltManager.sol b/bolt-contracts/src/interfaces/IBoltManager.sol index 1d134ca6a..fe1cc1b25 100644 --- a/bolt-contracts/src/interfaces/IBoltManager.sol +++ b/bolt-contracts/src/interfaces/IBoltManager.sol @@ -8,6 +8,7 @@ interface IBoltManager { error OperatorAlreadyRegistered(); error OperatorNotRegistered(); error UnauthorizedMiddleware(); + error InactiveOperator(); struct Operator { string rpc; @@ -35,6 +36,14 @@ interface IBoltManager { function validators() external view returns (IBoltValidators); + function getProposerStatus( + bytes32 pubkeyHash + ) external view returns (IBoltValidators.ProposerStatus memory status); + + function getProposerStatuses( + bytes32[] calldata pubkeyHashes + ) external view returns (IBoltValidators.ProposerStatus[] memory statuses); + function isOperatorAuthorizedForValidator(address operator, bytes32 pubkeyHash) external view returns (bool); function getSupportedRestakingProtocols() external view returns (address[] memory middlewares); diff --git a/bolt-contracts/src/interfaces/IBoltValidators.sol b/bolt-contracts/src/interfaces/IBoltValidators.sol index d481ee3ba..c871afe11 100644 --- a/bolt-contracts/src/interfaces/IBoltValidators.sol +++ b/bolt-contracts/src/interfaces/IBoltValidators.sol @@ -19,12 +19,19 @@ interface IBoltValidators { address controller; } + /// @notice Proposer status info. struct ProposerStatus { + // The pubkey hash of the validator. bytes32 pubkeyHash; + // Whether the corresponding operator is active based on collateral requirements. bool active; + // The operator address that is authorized to make & sign commitments on behalf of the validator. address operator; + // The operator RPC endpoint. string operatorRPC; + // The addresses of the collateral tokens. address[] collaterals; + // The corresponding amounts of the collateral tokens. uint256[] amounts; } diff --git a/bolt-contracts/test/BoltManager.EigenLayer.t.sol b/bolt-contracts/test/BoltManager.EigenLayer.t.sol index ddecb674a..6ba5f7d17 100644 --- a/bolt-contracts/test/BoltManager.EigenLayer.t.sol +++ b/bolt-contracts/test/BoltManager.EigenLayer.t.sol @@ -245,7 +245,7 @@ contract BoltManagerEigenLayerTest is Test { validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); } - IBoltValidators.ProposerStatus[] memory statuses = manager.getProposersStatus(pubkeyHashes); + IBoltValidators.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); assertEq(statuses.length, 10); } diff --git a/bolt-contracts/test/BoltManager.Symbiotic.t.sol b/bolt-contracts/test/BoltManager.Symbiotic.t.sol index 884765bdf..3c28ec457 100644 --- a/bolt-contracts/test/BoltManager.Symbiotic.t.sol +++ b/bolt-contracts/test/BoltManager.Symbiotic.t.sol @@ -360,7 +360,7 @@ contract BoltManagerSymbioticTest is Test { vm.warp(block.timestamp + EPOCH_DURATION * 2 + 1); assertEq(vault.currentEpoch(), 2); - IBoltValidators.ProposerStatus[] memory statuses = manager.getProposersStatus(pubkeyHashes); + IBoltValidators.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); assertEq(statuses.length, 10); }