Skip to content

Commit 1fcdc00

Browse files
committed
Add CapLens
1 parent 126298b commit 1fcdc00

7 files changed

Lines changed: 323 additions & 15 deletions

File tree

contracts/delegation/providers/eigenlayer/EigenServiceManager.sol

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ contract EigenServiceManager is IEigenServiceManager, UUPSUpgradeable, Access, E
260260
if (operatorShares[0] < 1e9) return 0;
261261

262262
address _oracle = $.oracle;
263-
(delegation,) = _coverageByStrategy(_operator, _strategy, _oracle);
263+
(delegation,) = coverageByStrategy(_operator, _strategy, _oracle);
264264
}
265265

266266
/// @inheritdoc IEigenServiceManager
@@ -274,7 +274,7 @@ contract EigenServiceManager is IEigenServiceManager, UUPSUpgradeable, Access, E
274274
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
275275
CachedOperatorData storage operatorData = $.operators[_operator];
276276
if (operatorData.strategy == address(0)) return 0;
277-
return _slashableCollateralByStrategy(_operator, operatorData.strategy);
277+
return slashableCollateralByStrategy(_operator, operatorData.strategy);
278278
}
279279

280280
/// @inheritdoc IEigenServiceManager
@@ -299,6 +299,11 @@ contract EigenServiceManager is IEigenServiceManager, UUPSUpgradeable, Access, E
299299
return operatorData.strategy;
300300
}
301301

302+
/// @inheritdoc IEigenServiceManager
303+
function oracle() external view returns (address) {
304+
return getEigenServiceManagerStorage().oracle;
305+
}
306+
302307
/// @inheritdoc IEigenServiceManager
303308
function eigenAddresses() external view returns (EigenAddresses memory) {
304309
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
@@ -455,11 +460,8 @@ contract EigenServiceManager is IEigenServiceManager, UUPSUpgradeable, Access, E
455460
}
456461
}
457462

458-
/// @notice Get the slashable collateral for a given operator and strategy
459-
/// @param _operator The operator address
460-
/// @param _strategy The strategy address
461-
/// @return The slashable collateral
462-
function _slashableCollateralByStrategy(address _operator, address _strategy) private view returns (uint256) {
463+
/// @inheritdoc IEigenServiceManager
464+
function slashableCollateralByStrategy(address _operator, address _strategy) public view returns (uint256) {
463465
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
464466
address collateralAddress = address(IStrategy(_strategy).underlyingToken());
465467
uint8 decimals = IERC20Metadata(collateralAddress).decimals();
@@ -471,14 +473,9 @@ contract EigenServiceManager is IEigenServiceManager, UUPSUpgradeable, Access, E
471473
return collateralValue;
472474
}
473475

474-
/// @notice Get the coverage for a given operator and strategy
475-
/// @param _operator The operator address
476-
/// @param _strategy The strategy address
477-
/// @param _oracle The oracle address
478-
/// @return collateralValue The collateral value
479-
/// @return collateralAmount The collateral amount
480-
function _coverageByStrategy(address _operator, address _strategy, address _oracle)
481-
private
476+
/// @inheritdoc IEigenServiceManager
477+
function coverageByStrategy(address _operator, address _strategy, address _oracle)
478+
public
482479
view
483480
returns (uint256 collateralValue, uint256 collateralAmount)
484481
{

contracts/delegation/providers/symbiotic/SymbioticNetworkMiddleware.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,16 @@ contract SymbioticNetworkMiddleware is
212212
collateralAddress = IVault(vault).collateral();
213213
}
214214

215+
/// @inheritdoc ISymbioticNetworkMiddleware
216+
function network() external view returns (address) {
217+
return getSymbioticNetworkMiddlewareStorage().network;
218+
}
219+
220+
/// @inheritdoc ISymbioticNetworkMiddleware
221+
function oracle() external view returns (address) {
222+
return getSymbioticNetworkMiddlewareStorage().oracle;
223+
}
224+
215225
/// @dev Get vault info
216226
/// @param _network Network address
217227
/// @param _agent Agent address

contracts/interfaces/IEigenServiceManager.sol

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,27 @@ interface IEigenServiceManager {
134134
*/
135135
function coverage(address operator) external view returns (uint256);
136136

137+
/**
138+
* @notice Get the coverage for a given operator and strategy
139+
* @param _operator The operator address
140+
* @param _strategy The strategy address
141+
* @param _oracle The oracle address
142+
* @return collateralValue The collateral value
143+
* @return collateral The collateral
144+
*/
145+
function coverageByStrategy(address _operator, address _strategy, address _oracle)
146+
external
147+
view
148+
returns (uint256 collateralValue, uint256 collateral);
149+
150+
/**
151+
* @notice Get the slashable collateral for a given operator and strategy
152+
* @param _operator The operator address
153+
* @param _strategy The strategy address
154+
* @return The slashable collateral
155+
*/
156+
function slashableCollateralByStrategy(address _operator, address _strategy) external view returns (uint256);
157+
137158
/**
138159
* @notice Registers an operator to the AVS, called by the Allocation Manager contract (access control set for the allocation manager).
139160
* @param _operator The operator to register
@@ -216,6 +237,11 @@ interface IEigenServiceManager {
216237
*/
217238
function operatorToStrategy(address operator) external view returns (address);
218239

240+
/**
241+
* @notice Returns the oracle address used for pricing
242+
*/
243+
function oracle() external view returns (address);
244+
219245
/**
220246
* @notice Returns the operator set id for an operator
221247
* @param operator The operator to get the operator set id for

contracts/interfaces/ISymbioticNetworkMiddleware.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,10 @@ interface ISymbioticNetworkMiddleware {
184184
/// @param _agent Agent address
185185
/// @return collateralAddress Collateral address
186186
function collateral(address _agent) external view returns (address collateralAddress);
187+
188+
/// @notice Symbiotic network address
189+
function network() external view returns (address);
190+
191+
/// @notice Oracle address used for pricing
192+
function oracle() external view returns (address);
187193
}

contracts/lens/CapLens.sol

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.28;
3+
4+
import { IDelegation } from "../interfaces/IDelegation.sol";
5+
import { IEigenServiceManager } from "../interfaces/IEigenServiceManager.sol";
6+
import { ISymbioticNetworkMiddleware } from "../interfaces/ISymbioticNetworkMiddleware.sol";
7+
8+
/// @title CapLens
9+
/// @notice Immutable lens to query slashable collateral and coverage by agent and epoch diff. Works for both Symbiotic and EigenLayer agents.
10+
contract CapLens {
11+
IDelegation public immutable delegation;
12+
13+
constructor(address _delegation) {
14+
delegation = IDelegation(_delegation);
15+
}
16+
17+
/// @notice Slashable collateral for an agent at (current epoch + epochDiff). Works for both Symbiotic and EigenLayer agents.
18+
/// @param _agent Agent address
19+
/// @param _epochDiff Epoch offset from current (0 = current epoch, +1 = next, -1 = previous)
20+
/// @return collateralValue Slashable collateral value in USD (8 decimals)
21+
/// @return collateral Slashable collateral amount in vault/strategy token units (Symbiotic only; 0 for Eigen as slashableCollateralByStrategy returns value only)
22+
function slashableCollateral(address _agent, int8 _epochDiff)
23+
external
24+
view
25+
returns (uint256 collateralValue, uint256 collateral)
26+
{
27+
(
28+
bool isEigen,
29+
address middleware,
30+
address vault,
31+
address network,
32+
address oracle,
33+
uint48 timestamp,
34+
address strategy
35+
) = _resolveParams(_agent, _epochDiff);
36+
if (middleware == address(0)) return (0, 0);
37+
38+
if (isEigen) {
39+
if (strategy == address(0)) return (0, 0);
40+
collateralValue = IEigenServiceManager(middleware).slashableCollateralByStrategy(_agent, strategy);
41+
return (collateralValue, 0);
42+
}
43+
44+
return
45+
ISymbioticNetworkMiddleware(middleware)
46+
.slashableCollateralByVault(network, _agent, vault, oracle, timestamp);
47+
}
48+
49+
/// @notice Coverage for an agent at (current epoch + epochDiff). Works for both Symbiotic and EigenLayer agents.
50+
/// @param _agent Agent address
51+
/// @param _epochDiff Epoch offset from current (0 = current epoch, +1 = next, -1 = previous)
52+
/// @return collateralValue Coverage value in USD (8 decimals)
53+
/// @return collateral Coverage amount in vault/strategy token units
54+
function coverage(address _agent, int8 _epochDiff)
55+
external
56+
view
57+
returns (uint256 collateralValue, uint256 collateral)
58+
{
59+
(
60+
bool isEigen,
61+
address middleware,
62+
address vault,
63+
address network,
64+
address oracle,
65+
uint48 timestamp,
66+
address strategy
67+
) = _resolveParams(_agent, _epochDiff);
68+
if (middleware == address(0)) return (0, 0);
69+
70+
if (isEigen) {
71+
if (strategy == address(0)) return (0, 0);
72+
return IEigenServiceManager(middleware).coverageByStrategy(_agent, strategy, oracle);
73+
}
74+
75+
return ISymbioticNetworkMiddleware(middleware).coverageByVault(network, _agent, vault, oracle, timestamp);
76+
}
77+
78+
/// @dev Resolve middleware type and all params for either Symbiotic or Eigen.
79+
/// @return isEigen True if the agent is on EigenLayer, false if Symbiotic
80+
/// @return middleware Middleware address
81+
/// @return vault For Symbiotic: vault address; for Eigen: address(0)
82+
/// @return network For Symbiotic: network address; for Eigen: address(0)
83+
/// @return oracle For Symbiotic: middleware oracle; for Eigen: IEigenServiceManager(middleware).oracle()
84+
/// @return timestamp For Symbiotic: timestamp; for Eigen: 0
85+
/// @return strategy For Eigen: operator's strategy; for Symbiotic: address(0)
86+
function _resolveParams(address _agent, int8 _epochDiff)
87+
internal
88+
view
89+
returns (
90+
bool isEigen,
91+
address middleware,
92+
address vault,
93+
address network,
94+
address oracle,
95+
uint48 timestamp,
96+
address strategy
97+
)
98+
{
99+
middleware = delegation.networks(_agent);
100+
if (middleware == address(0)) return (false, address(0), address(0), address(0), address(0), 0, address(0));
101+
102+
uint256 epochDuration = delegation.epochDuration();
103+
uint256 currentEpoch = block.timestamp / epochDuration;
104+
int256 targetEpoch = int256(currentEpoch) + int256(int8(_epochDiff));
105+
if (targetEpoch >= 0) timestamp = uint48(uint256(targetEpoch) * epochDuration) + 1;
106+
107+
try ISymbioticNetworkMiddleware(middleware).vaults(_agent) returns (address v) {
108+
vault = v;
109+
} catch { }
110+
111+
if (vault == address(0)) {
112+
strategy = IEigenServiceManager(middleware).operatorToStrategy(_agent);
113+
oracle = IEigenServiceManager(middleware).oracle();
114+
return (true, middleware, address(0), address(0), oracle, timestamp, strategy);
115+
} else {
116+
network = ISymbioticNetworkMiddleware(middleware).network();
117+
oracle = ISymbioticNetworkMiddleware(middleware).oracle();
118+
return (false, middleware, vault, network, oracle, timestamp, address(0));
119+
}
120+
}
121+
}

test/mocks/MockNetworkMiddleware.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ contract MockNetworkMiddleware is ISymbioticNetworkMiddleware {
8686
return _storage.agentsToVault[_agent];
8787
}
8888

89+
function network() external view returns (address) {
90+
return _storage.network;
91+
}
92+
93+
function oracle() external view returns (address) {
94+
return _storage.oracle;
95+
}
96+
8997
function distributeRewards(address _agent, address _token) external {
9098
// Mock implementation - no-op
9199
}

0 commit comments

Comments
 (0)