Skip to content

Commit

Permalink
Merge pull request #4 from ChorusOne/network-wide-metrics
Browse files Browse the repository at this point in the history
Export network-wide tokenomics metrics
  • Loading branch information
mksh authored Sep 25, 2024
2 parents c8cab29 + 19e13bb commit eb67a7e
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 8 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ Dimensions:
- `state` one of `active`, `inactive`, `liquidated`
- `operators` comma separated list of cluster operators integer identifiers in SSV system

----------

`ssv_network_fee` -- current SSV network fee as described in [https://docs.ssv.network/learn/protocol-overview/tokenomics/fees#k4tw9to38r3v](SSV tokenomics docs)

Dimensions:
- `network` one of `mainnet`, `holesky`

----------

`ssv_minimum_liquidation_collateral` -- current SSV minimum liquidation collateral as described in [https://docs.ssv.network/learn/protocol-overview/tokenomics/liquidations#minimum-liquidation-collateral](SSV tokenomics docs)

Dimensions:
- `network` one of `mainnet`, `holesky`


----------

`ssv_liquidation_threshold_period` -- current SSV liquidation threshold period as described in [https://docs.ssv.network/learn/protocol-overview/tokenomics/liquidations#liquidation-threshold-period](SSV tokenomics docs)

Dimensions:
- `network` one of `mainnet`, `holesky`



Installation
Expand Down
97 changes: 89 additions & 8 deletions ssv_cluster_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ def get_ssv_network_views_contract(
)


ssv_network_fee = Gauge(
name="ssv_network_fee",
documentation="Current SSV network fee in SSV tokens",
labelnames=["network"],
)
ssv_minimum_liquidation_collateral = Gauge(
name="ssv_minimum_liquidation_collateral",
documentation="Current minimum liquidation collateral for SSV network",
labelnames=["network"],
)
ssv_liquidation_threshold_period = Gauge(
name="ssv_liquidation_threshold_period",
documentation="SSV liquidation threshold period, number of blocks that should be always funded",
labelnames=["network"],
)


# #############
# Command line
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -185,8 +202,49 @@ class SSVAPIError(Exception):
]


class SSVNetworkProperties(BaseModel):

network_fee: int
minimum_liquidation_collateral: int
liquidation_threshold_period: int


class SSVNetworkContract(BaseModel):
"""A facade for web3 contract data retrieval for network wide values."""

network_views: AsyncContract

class Config:
arbitrary_types_allowed = True

async def fetch_network_fee(self) -> int:
return int(await self.network_views.functions.getNetworkFee().call())

async def fetch_minimum_liquidation_collateral(self) -> int:
return int(
await self.network_views.functions.getMinimumLiquidationCollateral().call()
)

async def fetch_liquidation_threshold_period(self) -> int:
return int(
await self.network_views.functions.getLiquidationThresholdPeriod().call()
)

async def fetch_all(self) -> SSVNetworkProperties:
(n_f, m_l_c, l_t_p) = await asyncio.gather(
self.fetch_network_fee(),
self.fetch_minimum_liquidation_collateral(),
self.fetch_liquidation_threshold_period(),
)
return SSVNetworkProperties(
network_fee=n_f,
minimum_liquidation_collateral=m_l_c,
liquidation_threshold_period=l_t_p,
)


class SSVClusterContract(BaseModel):
"""A facade for web3 contract data retrieval."""
"""A facade for web3 contract data retrieval for clusters."""

network_views: AsyncContract
clusters: set[SSVCluster]
Expand Down Expand Up @@ -348,8 +406,8 @@ async def fetch_clusters_info(self) -> list[SSVCluster]:

return clusters

def update_metrics(self, *clusters: SSVCluster) -> None:
"""Update metrics for Prometheus consumption."""
def update_clusters_metrics(self, *clusters: SSVCluster) -> None:
"""Update cluster related metrics for Prometheus consumption."""
for cluster in clusters:
labels = [
cluster.clusterId,
Expand All @@ -365,15 +423,38 @@ def update_metrics(self, *clusters: SSVCluster) -> None:
)
ssv_cluster_validators_count.labels(*labels).set(cluster.validatorCount)

def update_network_metrics(self, network_properties: SSVNetworkProperties) -> None:
"""Update network-wide metrics with value retrieven from contract."""
ssv_network_fee.labels(self.network).set(network_properties.network_fee)
ssv_liquidation_threshold_period.labels(self.network).set(
network_properties.liquidation_threshold_period
)
ssv_minimum_liquidation_collateral.labels(self.network).set(
network_properties.minimum_liquidation_collateral
)

async def clusters_updates(self) -> None:
"""Run cluster-specific metrics update."""
clusters = set(await self.fetch_clusters_info())
latest_metric_fetcher = SSVClusterContract(
network_views=self.network_views, clusters=clusters
)
await latest_metric_fetcher.fetch_all()
self.update_clusters_metrics(*clusters)

async def network_updates(self) -> None:
"""Run network-wide metrics update."""
network_metric_fetcher = SSVNetworkContract(network_views=self.network_views)
network_properties = await network_metric_fetcher.fetch_all()
self.update_network_metrics(network_properties)

async def tick(self) -> None:
"""Perform single data retrieval and metrics update."""
try:
clusters = set(await self.fetch_clusters_info())
latest_metric_fetcher = SSVClusterContract(
network_views=self.network_views, clusters=clusters
await asyncio.gather(
self.clusters_updates(),
self.network_updates(),
)
await latest_metric_fetcher.fetch_all()
self.update_metrics(*clusters)
except Exception:
logger.exception("Failed to update cluster details")

Expand Down
11 changes: 11 additions & 0 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,19 @@ async def test_metrics(metrics_server: str) -> None:
sample.labels["owner"] == "0xD4BB555d3B0D7fF17c606161B44E372689C14F4B"
)
matched_metrics.add(metric.name)
elif metric.name in (
"ssv_network_fee",
"ssv_minimum_liquidation_collateral",
"ssv_liquidation_threshold_period",
):
sample = metric.samples[0]
assert sample.labels["network"] == "holesky"
matched_metrics.add(metric.name)
assert matched_metrics == {
"ssv_cluster_validators_count",
"ssv_cluster_balance",
"ssv_cluster_burn_rate",
"ssv_network_fee",
"ssv_minimum_liquidation_collateral",
"ssv_liquidation_threshold_period",
}

0 comments on commit eb67a7e

Please sign in to comment.