DataClient is the typed access layer over the
data moat — the first structured view into the
microstructure of the DeFi LP volatility-risk premium. It is read-only: it
holds no signer, sends no transaction, and carries no claim.
Its job is to expose the five non-circular behavioral
signals through one consistent rule: if a backing (subgraph URL,
RPC, or telemetry sink) is absent, the surface degrades to a typed pending state — never
an error.
The surface splits cleanly into two classes:
LIVE — live RPC
getCurrentLoadSurface and getSurfaceSigmaRef hit the chain directly and return
real numbers today. The current load surface is the one live half of
Signal 1 — the “price-to-beat”.LIVE — subgraph/API-backed
The five history/aggregate methods resolve from the deployed subgraph via the hosted
API. If a history source is absent the SDK returns a typed
ApiPending envelope
naming the API route and the query — so the UI degrades gracefully rather than throws.Why this split exists. The on-chain moat events (
SwapPriced, QuoteFilled) and
the off-chain telemetry sinks are live now, and the subgraph that indexes them into
bucketed history is deployed and indexing (served via the hosted API). So both the
current surface and history / aggregates are queryable today; if a backing is
absent a surface degrades gracefully rather than throws. See
State of the dataset.Method reference
Construct via the one-call factory;sdk.data needs only a
read client.
The data moat is free public infrastructure, not a paywall. Revenue is protocol
fees; the data is the competitive moat.
DataClient is the
SDK rung of the three-layer access rule — needs the exact surface right now → SDK;
needs history or an aggregate → subgraph (consumed via the API); needs frictionless
public access with no wallet → API.The one live surface — getCurrentLoadSurface
getCurrentLoadSurface is the only DataClient method that hits live RPC instead
of the API. It answers the question an MM or an LP needs right
now: what mechanical load is the cvAMM pool charging — the
“price-to-beat”? It fans the exact read set that MM getMarketPricing runs, across a
set of markets at once.
What it reads (per market, one multicall)
1
Read loadParams + inventory once (pool-wide)
InflexionCore.loadParams() (the load-stack curve params) and
ConvexityVault.inventory() → (total, locked, free, util, conc) are pool-wide, so
they are read a single time and shared across all markets.2
Per-market markets() multicall
InflexionCore.markets(marketId) → the market config (token0/token1/fee/duration).
An unknown or inactive market yields an inlined degraded row, never a thrown call.3
Per-market fairPremium() multicall
FairValueOracle.fairPremium(token, aWad, bWad, durationSeconds, maxIL) →
(premium, fairRateWad, sigmaRefWad). This is the on-chain
Phi-sum — never reimplemented off-chain.4
Finish the load stack client-side (parity-locked TS port)
The deterministic,
pure load stack is evaluated in TypeScript
via the parity-locked cvamm port, then poolPremium = ceil(fairPremium·(1+totalLoad))
capped at MaxIL — exactly _pricePathAFromFair on-chain.Geometry is optional — the load % is geometry-independent
If a market entry supplies nogeometry, the SDK substitutes a neutral reference
geometry. This is sound because the load percentage is
geometry-independent — only the dollar fairPremium / poolPremium scale with the
position’s aWad/bWad/maxIL. So you can read the per-market
load surface without bringing any LP position. This is the same property that makes
MaxIL the unit of risk and an MM quote
per-market, never per-NFT.
Per-market graceful degradation
EachSurfaceRow is independently a discriminated union. A degraded market never
breaks the others:
oracle-degraded—OracleManager.getPricereverted (stale feed / L2 sequencer down). The oracle is designed to revert rather than serve a bad price.market-unknown— the marketId is not an active market (configtoken0 == 0).
Worked example — read the live load surface
data-client-surface.ts
loadBps) is the QuoteFilled event, which arrives through
the subgraph-backed history methods below.
getSurfaceSigmaRef — the transparent vol reference
The second live method reads the realized-vol oracle the entire load stack is measured
against:
σ_ref is a poke-based, time-aware EWMA of Chainlink-tick log-returns — never raw
realized σ (σ_ref derivation). Because it is transparent and
on-chain, the load charged above it is a well-defined, identifiable quantity — which
is precisely the property that converts the moat from circular to legible. It degrades to { available: false, reason: 'vol-uninitialized' } if the oracle has not yet been poked for that token.
The history methods + the ApiPending envelope
The remaining five methods need history or aggregates, which come from the
subgraph (consumed via the hosted API at https://inflexion-backend.onrender.com). They
resolve live today; if a history source is absent the method returns a typed
ApiPending body instead of throwing:
ApiPending names the route and the would-be query. This is the
graceful-degradation contract: if a backing is absent the UI can show
“coming from <route>” rather than erroring, and the SDK swaps a one-line body to
resolve from data — zero re-derivation. The SDK never fabricates history.available and render the pending state:
pending-history.ts
The subgraph is deployed and indexing — so every history/aggregate method resolves
live, with graceful degradation if a backing is absent.The contracts emit every event the moat needs (
SwapPriced, QuoteFilled), the
subgraph indexes them (slug inflexion-arb-sepolia on The Graph Studio, indexing from
the redeploy startBlock), and the hosted API at
https://inflexion-backend.onrender.com serves getLoadSurfaceHistory,
getQuoteCompetition, getDemandRequests, getNavHistory, and getNetGamma over that
subgraph plus the telemetry sinks. The off-chain telemetry halves (DEMAND_LOG,
COMPETITION_LOG) are capturing now. If a backing (subgraph URL, RPC, or telemetry
sink) is absent, the method returns ApiPending rather than throwing — so the degraded
path remains a one-line body swap to data. See SDK degradation.The free hosting tier sleeps after ~15 min idle, so the first request after a quiet
period may incur a brief cold start.
One method’s latent half is off-chain by design — getDemandRequests
getDemandRequests resolves live, but its latent half is off-chain by
design and can never be on-chain. It surfaces
Signal 4’s demand that includes unfilled interest — an LP who
priced but did not buy, and an MM quote that lost or was withdrawn, leave no on-chain
trace because invariant I7 guarantees an unchosen quote
touches no nonce and no capacity. Putting unfilled interest
on-chain would both cost gas and break I7. So the latent half lives in the engine’s
DEMAND_LOG telemetry sink — which is capturing now and is unreconstructable
retroactively. getDemandRequests resolves from that accumulating log via the hosted
engine (/engine), not from a backfill.
The five-signals access map
DataClient is the SDK rung of the per-signal access map. Each signal carries an
explicit non-circularity verdict — the moat’s credibility rests on not being
circular: a signal is non-circular iff its informative degrees of freedom come
from the MM’s loadBps or the LP’s geometry/duration choice, measured against the
transparent mechanical baseline, not derived from it. The
deliberately-dropped circular idea (“invert fairRate to read implied vol”) is not a
signal — see circular vs behavioral for why.
The behavioral − mechanical shape. Four of the five signals separate a
behavioral leg (an actor’s free choice) from a mechanical baseline (the
protocol’s transparent price-to-beat); the subtraction is what isolates the actor’s
view and removes the circular component:
getCurrentLoadSurface serves the mechanical leg live (Path-A totalLoadWad,
sigmaRefWad); the behavioral legs arrive through the history methods, served live from
the deployed subgraph’s indexed QuoteFilled events via the API.State of the dataset
This is the honest framing, and it is mandatory on every data-product surface.Structures ship from day one; the full DYNAMICS (pool-vs-MM spread as a forward-vol signal) mature with multiple competing MMs and volume. The dataset begins at the single redeploy; the off-chain telemetry halves begin at the first interaction logged.What Inflexion offers today is the architecture and the first public view into the microstructure of the DeFi LP volatility-risk premium — not yet a mature dataset:
- Structures are present day-one. All five non-circular behavioral signals are defined and queryable from launch.
- Dynamics mature with volume. The richer signal — the pool-vs-MM spread read as a forward-vol indicator — only sharpens once multiple market makers compete and real flow accumulates.
- The dataset begins at the single redeploy, and the off-chain telemetry halves begin at the first interaction logged. History is served live from the subgraph via the API; if a backing (subgraph URL / RPC / telemetry) is absent, surfaces degrade to a typed
pendingstate — never an error.
So the leading-indicator and forward-vol dynamics (the dispersion of Signal 1, the
competitive distribution of Signal 2) need ≥3 competing MMs and ~30–50 fills per
bucket — weeks-to-months at launch flow. The
structures — the current load surface, the term-structure shape, the demand skew by
geometry, the net-gamma surface — are queryable from launch. We ship the architecture
and the first view, not a mature dataset.
Degradation cheat-sheet
The envelopesDataClient returns:
The contract everywhere: reads are graceful —
DataClient never throws on a
reverting oracle, an absent rich event, or an absent history backing; it returns a
typed degraded envelope. See SDK degradation for
the full taxonomy.
Related
The data moat (concept)
Why the flow is the product, and the circular idea we deliberately dropped.
The five signals
Exhaustive per-signal definitions, sourcing, non-circularity verdicts, and maturity.
On-chain primitives
The two moat events (
SwapPriced, QuoteFilled) and the load-breakdown library.The access map
The three-layer SDK / subgraph / API routing rule, per signal.
The cvAMM port
The parity-locked TS load stack — the only permitted off-chain duplication.
SDK degradation
Every typed degraded envelope the SDK returns, and how to render each.