diff --git a/bolt-contracts/src/contracts/BoltManagerV3.sol b/bolt-contracts/src/contracts/BoltManagerV3.sol index 10aaf7770..59ff2aa90 100644 --- a/bolt-contracts/src/contracts/BoltManagerV3.sol +++ b/bolt-contracts/src/contracts/BoltManagerV3.sol @@ -7,8 +7,8 @@ import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {OperatorMapWithTimeV2} from "../lib/OperatorMapWithTimeV2.sol"; -import {EnumerableMapV2} from "../lib/EnumerableMapV2.sol"; +import {OperatorMapWithTimeV3} from "../lib/OperatorMapWithTimeV3.sol"; +import {EnumerableMapV3} from "../lib/EnumerableMapV3.sol"; import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; import {IBoltValidatorsV2} from "../interfaces/IBoltValidatorsV2.sol"; @@ -24,11 +24,11 @@ import {IBoltManagerV3} from "../interfaces/IBoltManagerV3.sol"; /// You can also validate manually with forge: forge inspect storage-layout --pretty contract BoltManagerV3 is IBoltManagerV3, OwnableUpgradeable, UUPSUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; - using EnumerableMapV2 for EnumerableMapV2.OperatorMap; - using OperatorMapWithTimeV2 for EnumerableMapV2.OperatorMap; + using EnumerableMapV3 for EnumerableMapV3.OperatorMap; + using OperatorMapWithTimeV3 for EnumerableMapV3.OperatorMap; // ========= STORAGE ========= - + /// @notice Start timestamp of the first epoch. uint48 public START_TIMESTAMP; @@ -40,7 +40,7 @@ contract BoltManagerV3 is IBoltManagerV3, OwnableUpgradeable, UUPSUpgradeable { IBoltValidatorsV2 public validators; /// @notice Set of operator addresses that have opted in to Bolt Protocol. - EnumerableMapV2.OperatorMap private operators; + EnumerableMapV3.OperatorMap private operators; /// @notice Set of restaking protocols supported. Each address corresponds to the /// associated Bolt Middleware contract. @@ -170,16 +170,16 @@ contract BoltManagerV3 is IBoltManagerV3, OwnableUpgradeable, UUPSUpgradeable { /// @return operatorData The operator data. function getOperatorData( address operator - ) public view returns (EnumerableMapV2.Operator memory operatorData) { + ) public view returns (EnumerableMapV3.Operator memory operatorData) { return operators.get(operator); } /// @notice Get the data of all registered operators. /// @return operatorData An array of operator data. - function getAllOperatorsData() public view returns (EnumerableMapV2.Operator[] memory operatorData) { - operatorData = new EnumerableMapV2.Operator[](operators.length()); + function getAllOperatorsData() public view returns (EnumerableMapV3.Operator[] memory operatorData) { + operatorData = new EnumerableMapV3.Operator[](operators.length()); for (uint256 i = 0; i < operators.length(); ++i) { - (address operator, EnumerableMapV2.Operator memory data) = operators.at(i); + (address operator, EnumerableMapV3.Operator memory data) = operators.at(i); operatorData[i] = data; } } @@ -210,7 +210,7 @@ contract BoltManagerV3 is IBoltManagerV3, OwnableUpgradeable, UUPSUpgradeable { // NOTE: this will revert when the proposer does not exist. IBoltValidatorsV2.ValidatorInfo memory validator = validators.getValidatorByPubkeyHash(pubkeyHash); - EnumerableMapV2.Operator memory operatorData = operators.get(validator.authorizedOperator); + EnumerableMapV3.Operator memory operatorData = operators.get(validator.authorizedOperator); status.pubkeyHash = pubkeyHash; status.operator = validator.authorizedOperator; @@ -245,7 +245,7 @@ contract BoltManagerV3 is IBoltManagerV3, OwnableUpgradeable, UUPSUpgradeable { /// @param collateral The address of the collateral asset to get the stake for. /// @return amount The amount staked by the operator for the given collateral asset. function getOperatorStake(address operator, address collateral) public view returns (uint256) { - EnumerableMapV2.Operator memory operatorData = operators.get(operator); + EnumerableMapV3.Operator memory operatorData = operators.get(operator); return IBoltMiddlewareV1(operatorData.middleware).getOperatorStake(operator, collateral); } @@ -258,7 +258,7 @@ contract BoltManagerV3 is IBoltManagerV3, OwnableUpgradeable, UUPSUpgradeable { ) public view returns (uint256 amount) { // Loop over all of the operators, get their middleware, and retrieve their staked amount. for (uint256 i = 0; i < operators.length(); ++i) { - (address operator, EnumerableMapV2.Operator memory operatorData) = operators.at(i); + (address operator, EnumerableMapV3.Operator memory operatorData) = operators.at(i); amount += IBoltMiddlewareV1(operatorData.middleware).getOperatorStake(operator, collateral); } @@ -276,7 +276,7 @@ contract BoltManagerV3 is IBoltManagerV3, OwnableUpgradeable, UUPSUpgradeable { } // Create an already enabled operator - EnumerableMapV2.Operator memory operator = EnumerableMapV2.Operator(rpc, msg.sender, Time.timestamp()); + EnumerableMapV3.Operator memory operator = EnumerableMapV3.Operator(rpc, msg.sender, Time.timestamp()); operators.set(operatorAddr, operator); } diff --git a/bolt-contracts/src/interfaces/IBoltManagerv3.sol b/bolt-contracts/src/interfaces/IBoltManagerv3.sol index f0a9dad9f..9499149f7 100644 --- a/bolt-contracts/src/interfaces/IBoltManagerv3.sol +++ b/bolt-contracts/src/interfaces/IBoltManagerv3.sol @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {EnumerableMapV2} from "../lib/EnumerableMapV2.sol"; +import {EnumerableMapV3} from "../lib/EnumerableMapV3.sol"; interface IBoltManagerV3 { error InvalidQuery(); error OperatorAlreadyRegistered(); error OperatorNotRegistered(); error UnauthorizedMiddleware(); - // TODO: remove in future upgrade (unused) - error InactiveOperator(); /// @notice Proposer status info. struct ProposerStatus { @@ -47,9 +45,9 @@ interface IBoltManagerV3 { function getOperatorData( address operator - ) external view returns (EnumerableMapV2.Operator memory operatorData); + ) external view returns (EnumerableMapV3.Operator memory operatorData); - function getAllOperatorsData() external view returns (EnumerableMapV2.Operator[] memory operatorData); + function getAllOperatorsData() external view returns (EnumerableMapV3.Operator[] memory operatorData); function getProposerStatus( bytes20 pubkeyHash diff --git a/bolt-contracts/src/lib/EnumerableMapV3.sol b/bolt-contracts/src/lib/EnumerableMapV3.sol index 5c9d79343..2985ec087 100644 --- a/bolt-contracts/src/lib/EnumerableMapV3.sol +++ b/bolt-contracts/src/lib/EnumerableMapV3.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.25; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -library EnumerableMapV2 { +library EnumerableMapV3 { using EnumerableSet for EnumerableSet.Bytes32Set; error KeyNotFound(address key); @@ -52,7 +52,7 @@ library EnumerableMapV2 { function get(OperatorMap storage self, address key) internal view returns (Operator memory) { if (!contains(self, key)) { - revert KeyNotFound(); + revert KeyNotFound(key); } return self._values[bytes32(uint256(uint160(key)))]; diff --git a/bolt-contracts/src/lib/OperatorMapWithTimeV3.sol b/bolt-contracts/src/lib/OperatorMapWithTimeV3.sol new file mode 100644 index 000000000..571f41d7f --- /dev/null +++ b/bolt-contracts/src/lib/OperatorMapWithTimeV3.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; + +import {EnumerableMapV3} from "./EnumerableMapV3.sol"; + +library OperatorMapWithTimeV3 { + using EnumerableMapV3 for EnumerableMapV3.OperatorMap; + + error AlreadyAdded(); + error NotEnabled(); + error AlreadyEnabled(); + + uint256 private constant ENABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 private constant DISABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF << 48; + + function add(EnumerableMapV3.OperatorMap storage self, address addr) internal { + if (!self.set(addr, EnumerableMapV3.Operator("", address(0), 0))) { + revert AlreadyAdded(); + } + } + + function disable(EnumerableMapV3.OperatorMap storage self, address addr) internal { + EnumerableMapV3.Operator memory operator = self.get(addr); + uint256 value = operator.timestamp; + + if (uint48(value) == 0 || uint48(value >> 48) != 0) { + revert NotEnabled(); + } + + value |= uint256(Time.timestamp()) << 48; + operator.timestamp = value; + self.set(addr, operator); + } + + function enable(EnumerableMapV3.OperatorMap storage self, address addr) internal { + EnumerableMapV3.Operator memory operator = self.get(addr); + uint256 value = operator.timestamp; + + if (uint48(value) != 0 && uint48(value >> 48) == 0) { + revert AlreadyEnabled(); + } + + value = uint256(Time.timestamp()); + operator.timestamp = value; + self.set(addr, operator); + } + + function atWithTimes( + EnumerableMapV3.OperatorMap storage self, + uint256 idx + ) internal view returns (address key, uint48 enabledTime, uint48 disabledTime) { + EnumerableMapV3.Operator memory value; + (key, value) = self.at(idx); + uint256 timestamp = value.timestamp; + enabledTime = uint48(timestamp); + disabledTime = uint48(timestamp >> 48); + } + + function getTimes( + EnumerableMapV3.OperatorMap storage self, + address addr + ) internal view returns (uint48 enabledTime, uint48 disabledTime) { + EnumerableMapV3.Operator memory value = self.get(addr); + enabledTime = uint48(value.timestamp); + disabledTime = uint48(value.timestamp >> 48); + } +}