diff --git a/bolt-contracts/foundry.toml b/bolt-contracts/foundry.toml index b19612e4a..8dd782afe 100644 --- a/bolt-contracts/foundry.toml +++ b/bolt-contracts/foundry.toml @@ -26,6 +26,7 @@ remappings = [ "@relic/=lib/relic-sdk/packages/contracts", "@symbiotic/=lib/core/src/", "@eigenlayer/=lib/eigenlayer-contracts/", + "@eigenlayer-middleware/=lib/eigenlayer-middleware/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/", diff --git a/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol index 828a3367a..9fe54b216 100644 --- a/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol +++ b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol @@ -14,8 +14,8 @@ import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; +import {IServiceManager} from "@eigenlayer-middleware/src/interfaces/IServiceManager.sol"; import {IStrategyManager} from "@eigenlayer/src/contracts/interfaces/IStrategyManager.sol"; -import {IServiceManager} from "@eigenlayer/src/contracts/interfaces/IServiceManager.sol"; import {IAVSDirectory} from "@eigenlayer/src/contracts/interfaces/IAVSDirectory.sol"; import {IDelegationManager} from "@eigenlayer/src/contracts/interfaces/IDelegationManager.sol"; import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; @@ -31,7 +31,7 @@ import {StrategyManagerStorage} from "@eigenlayer/src/contracts/core/StrategyMan /// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps /// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. /// You can also validate manually with forge: forge inspect storage-layout --pretty -contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UUPSUpgradeable { +contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, IServiceManager, OwnableUpgradeable, UUPSUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableMap for EnumerableMap.AddressToUintMap; using MapWithTimeData for EnumerableMap.AddressToUintMap; @@ -184,8 +184,7 @@ contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UU revert NotOperator(); } - // Register the operator to the AVS directory for this AVS - AVS_DIRECTORY.registerOperatorToAVS(msg.sender, operatorSignature); + registerOperatorToAVS(msg.sender, operatorSignature); // Register the operator in the manager manager.registerOperator(msg.sender, rpc); @@ -199,7 +198,7 @@ contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UU revert NotRegistered(); } - AVS_DIRECTORY.deregisterOperatorFromAVS(msg.sender); + deregisterOperatorFromAVS(msg.sender); manager.deregisterOperator(msg.sender); } @@ -257,8 +256,6 @@ contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UU uint48 epochStartTs = getEpochStartTs(getEpochAtTs(Time.timestamp())); - IStrategy[] memory strategyImpls = new IStrategy[](strategies.length()); - for (uint256 i = 0; i < strategies.length(); ++i) { (address strategy, uint48 enabledTime, uint48 disabledTime) = strategies.atWithTimes(i); @@ -271,14 +268,8 @@ contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UU address collateral = address(strategyImpl.underlyingToken()); collateralTokens[i] = collateral; - strategyImpls[i] = strategyImpl; - } - - // NOTE: order is preserved, which is why we can use the same index for both arrays below - uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares(operator, strategyImpls); - - for (uint256 i = 0; i < strategyImpls.length; ++i) { - amounts[i] = strategyImpls[i].sharesToUnderlyingView(shares[i]); + uint256 shares = DELEGATION_MANAGER.operatorShares(operator, strategyImpl); + amounts[i] = strategyImpl.sharesToUnderlyingView(shares); } return (collateralTokens, amounts); @@ -309,9 +300,6 @@ contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UU uint48 epochStartTs = getEpochStartTs(getEpochAtTs(timestamp)); - // NOTE: Can this be done more gas-efficiently? - IStrategy[] memory strategyMem = new IStrategy[](1); - for (uint256 i = 0; i < strategies.length(); i++) { (address strategy, uint48 enabledTime, uint48 disabledTime) = strategies.atWithTimes(i); @@ -323,10 +311,8 @@ contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UU continue; } - strategyMem[0] = IStrategy(strategy); - // NOTE: order is preserved i.e., shares[i] corresponds to strategies[i] - uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares(operator, strategyMem); - amount += IStrategy(strategy).sharesToUnderlyingView(shares[0]); + uint256 shares = DELEGATION_MANAGER.operatorShares(operator, IStrategy(strategy)); + amount += IStrategy(strategy).sharesToUnderlyingView(shares); } return amount; @@ -352,4 +338,61 @@ contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UU function _wasEnabledAt(uint48 enabledTime, uint48 disabledTime, uint48 timestamp) private pure returns (bool) { return enabledTime != 0 && enabledTime <= timestamp && (disabledTime == 0 || disabledTime >= timestamp); } + + // ============== EIGENLAYER SERVICE MANAGER ================= // + // Cfr. https://docs.eigenlayer.xyz/developers/avs-dashboard-onboarding + // getOperatorRestakedStrategies and getRestakeableStrategies have reference implementations + // that read from RegistryCoordinator & StakeRegistry. These are middleware contracts that + // are not used in the EigenLayer operator CLI as of today (23 Oct 2024): https://github.com/Layr-Labs/eigensdk-go/blob/0042b1a0dd502bb03c6bf1da85fc096c5c8e8f1b/chainio/clients/elcontracts/writer.go#L158 + // + // So we'll just get that information from our own system for now. + + function registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) public override { + // Register the operator to the AVS directory for this AVS + AVS_DIRECTORY.registerOperatorToAVS(operator, operatorSignature); + } + + function deregisterOperatorFromAVS( + address operator + ) public override { + // NOTE: need to do this check because these functions have to be public + if (msg.sender != operator) { + revert NotAllowed(); + } + + AVS_DIRECTORY.deregisterOperatorFromAVS(operator); + } + + function getOperatorRestakedStrategies( + address operator + ) external view override returns (address[] memory) { + address[] memory restakedStrategies = new address[](strategies.length()); + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(Time.timestamp())); + + for (uint256 i = 0; i < strategies.length(); ++i) { + (address strategy, uint48 enabledTime, uint48 disabledTime) = strategies.atWithTimes(i); + + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + continue; + } + + if (DELEGATION_MANAGER.operatorShares(operator, IStrategy(strategy)) > 0) { + restakedStrategies[restakedStrategies.length] = strategy; + } + } + + return restakedStrategies; + } + + function getRestakeableStrategies() external view override returns (address[] memory) { + return strategies.keys(); + } + + function avsDirectory() external view override returns (address) { + return address(AVS_DIRECTORY); + } }