CheckPoolHealth
CheckPoolHealth is a read-only, stateless primitive that takes a snapshot of pool-level health metrics. Read-only structured measurement — no derivation, no thresholds; that’s DetectRugSignals’s job.
V2 / V3 only — Balancer/Stableswap don’t map cleanly to the pool-health framing (LP concentration is a different question for weighted/amplified pools).
Signature at a glance
Section titled “Signature at a glance”| Protocol | Required call shape |
|---|---|
| Uniswap V2 | CheckPoolHealth().apply(lp, recent_window=20) |
| Uniswap V3 | CheckPoolHealth().apply(lp, recent_window=20) (some fields are V2-only) |
| Balancer | ❌ N/A |
| Stableswap | ❌ N/A — depeg framing instead, see AssessDepegRisk |
Common parameters
Section titled “Common parameters”| Parameter | Type | Description |
|---|---|---|
lp | UniswapExchange | V2 or V3 LP exchange. |
recent_window | int (default 20) | V2-only — number of recent swaps to average for fee_accrual_rate_recent. Ignored on V3. |
What this measures
Section titled “What this measures”reserve0,reserve1— current pool reserves, human unitstvl_in_token0— total value locked, denominated in token0 (reserve0 + reserve1 / spot_price)spot_price— current marginal price (token1 per token0)total_liquidity— pool’s total LP supplycollected_fees— cumulative fees, from the running accumulator (V2: per-swap history; V3: globalX128 accumulator)num_swaps— V2-only count of swap events (V3 lacks per-swap history →None)fee_accrual_rate_recent— V2-only fee rate over the lastrecent_windowswaps (V3 →None)num_lps— count ofliquidity_providersexcluding the V2MINIMUM_LIQUIDITYburn sentinel address ("0")top_lp_share_pct— share of LP supply held by the largest holder (excluding the sentinel).Nonewhen the pool has no real LPs.has_activity— V2 only: heuristic boolean fromnum_swaps > 0and recent fee accrual
The measurement reads pool state directly. No fixed-points, no closed-form derivations — just a structured view of what the pool’s already tracking. Calling this math would oversell what the code does.
V3 partial coverage. Two fields are V2-only: num_swaps and fee_accrual_rate_recent. Per-swap history isn’t part of the V3 exchange’s interface; only the cumulative accumulator is. This is a real surface gap, not a planning issue.
MINIMUM_LIQUIDITY sentinel. V2 burns a small initial liquidity to a sentinel address ("0") on first mint to prevent share-price manipulation. The primitive excludes this sentinel from liquidity_providers counts and top_lp_share_pct — counting it would inflate apparent concentration.
Example
Section titled “Example”from defipy import CheckPoolHealthfrom defipy.twin import MockProvider, StateTwinBuilder
provider = MockProvider()builder = StateTwinBuilder()lp_v2 = builder.build(provider.snapshot("eth_dai_v2"))
result = CheckPoolHealth().apply(lp_v2)
print(f"version: {result.version}")print(f"pair: {result.token0_name}/{result.token1_name}")print(f"spot_price: {result.spot_price:.4f}")print(f"tvl_in_token0: {result.tvl_in_token0:.4f}")print(f"num_swaps / num_lps: {result.num_swaps} / {result.num_lps}")print(f"top_lp_share_pct: {result.top_lp_share_pct}")print(f"has_activity: {result.has_activity}")A freshly-initialized pool: 0 swaps, 1 LP, that LP holds 100% of supply. has_activity = False because no fees have accrued.
How this composes
Section titled “How this composes”- Composed into by
DetectRugSignals— that primitive depth-chains over this one to apply threshold comparators and produce a count-based risk bucket. - Composed into by
CompareFeeTiersfor the per-tier TVL and fee-yield inputs.
See also
Section titled “See also”DetectRugSignals— threshold-based risk detector composed over this primitiveDetectFeeAnomaly— invariant-vs-contract consistency check (different question)CheckTickRangeStatus— adjacent V3 measurement (range-status)- The Primitive Contract — cross-cutting invariants
- MCP tool exposure: Curated v2.0 toolset.