Skip to main content
Every one of the five behavioral signals is built from the same three primitives, recombined: a load (the price above fair value), a σ_ref (the transparent realized-vol reference the load is measured against), and a geometry (the specific Uniswap v3 position the LP brought). Pin these three down once and every signal becomes a recipe over them — and the circularity verdicts become mechanical. The pattern that unifies them: a signal is behavioral (and therefore non-circular) exactly when its informative degree of freedom comes from an actor’s free choice — an MM’s loadBps or an LP’s geometry/duration choice — measured against the transparent mechanical baseline, never derived from it. The load is where that choice lives; σ_ref is the yardstick; geometry is the bucket.

Load

The dealer markup over published fair value. Mechanical on Path A (a pure function of pool state); a free choice on Path B (the MM’s loadBps).

σ_ref

The on-chain EWMA realized-vol oracle. Transparent and published — which is what makes the load charged above it a well-defined, identifiable quantity.

Geometry

Width × distance-to-edge × duration. The LP’s free choice of what to protect; the bucket every signal is keyed by.

1. Load — the price above fair value

Every premium in the protocol is the published fair value multiplied by one plus a markup, hard-capped at MaxIL: premium=FairPremium⋅(1+totalLoad),premium≤MaxIL.\text{premium} = \text{FairPremium} \cdot \bigl(1 + \text{totalLoad}\bigr), \qquad \text{premium} \le \text{MaxIL}. The totalLoad is the dealer markup over the protocol’s published fair value — the part of the price the protocol does not compute from the option math. Where it comes from depends on the rail (the two rails):
On the cvAMM pool the load is a deterministic, on-chain pure function of pool state — fully reproducible from public getters. This is the mechanical baseline, the price-to-beat. Exactly as deployed:totalLoad=clamp⁡(baseLoad(σref)⏟regime band+utilSkew(u)⏟scarcity+dispSkew(h)⏟concentration,  maxLoad)\text{totalLoad} = \operatorname{clamp}\Bigl( \underbrace{\text{baseLoad}(\sigma_{\text{ref}})}_{\text{regime band}} + \underbrace{\text{utilSkew}(u)}_{\text{scarcity}} + \underbrace{\text{dispSkew}(h)}_{\text{concentration}}, \; \text{maxLoad} \Bigr)
  • baseLoad — a σ_ref regime band (calm / normal / stressed), not a continuous function of σ_ref.
  • utilSkew(u) — flat below a knee, convex above, capped, on u = locked / total.
  • dispSkew(h) — min(dispCap, dispSlope · h^dispPower), where h is the per-market coverage HHI (concentration).
  • The I10 clamp — total = min(base + util + disp, maxLoadBps·1e14). This is invariant I10 by construction: premium ≤ FairPremium·(1+maxLoad) for any inputs, on both rails, upstream of settle.
No load primitive is hardcoded. Every curve parameter (baseLoad regime bands, the util-skew knee/slope/power/cap, the dispersion-skew slope/power/cap, maxLoadBps) comes from the calibrated risk-parameter set (the cvAMM block), mirrored on-chain by InflexionCore.loadParams(). This matters for the moat: the mechanical baseline is fully reproducible from public getters, which is exactly what makes the behavioral MM load measurable against it.

1.1 The duration-independence subtlety of the Path-A load

This is the single subtlety that decides how the term-structure signal behaves, and it is easy to get wrong. The Path-A load stack is duration-INDEPENDENT by construction — none of baseLoadWad, utilSkewWad, dispSkewWad takes durationSeconds as an argument. All maturity dependence lives elsewhere — in fairRate, the exact closed-form Φ-sum, which is the only term carrying the σ_ref·√T dependence. The factorization is clean: premium(T)=fairRate(a,b,σref,T)⏟all the time dependence⋅MaxIL⏟geometry, frozen⋅(1+totalLoad(σref,u,h)⏟no T).\text{premium}(T) = \underbrace{\text{fairRate}(a,b,\sigma_{\text{ref}},T)}_{\text{all the time dependence}} \cdot \underbrace{\text{MaxIL}}_{\text{geometry, frozen}} \cdot \bigl(1 + \underbrace{\text{totalLoad}(\sigma_{\text{ref}}, u, h)}_{\text{no }T}\bigr). The consequence for the moat is precise:
A Path-A “term structure of load” is a flat line.
  • Limit — if you plot SwapPriced.totalLoadWad (the pool load) across the 7/30/90d markets for a fixed geometry, you get a horizontal line. The pool load does not move with maturity.
  • Why — the load stack is pure in (σ_ref, util, conc) only; durationSeconds never enters it. All maturity dependence is folded into fairRate(...,T), the protocol’s own σ_ref·√T closed form — which is mechanical and circular, not a behavioral signal.
  • Mitigation / framing — the only behavioral term structure is the MM’s loadBps slope across 7/30/90d for one range. Path A gives you the published fairRate term structure (mechanical); Path B gives the behavioral load term structure. Do not conflate them.
So when Signal 3 (term structure of convexity) measures a slope, the slope it cares about is QuoteFilled.loadBps across durations — an MM choice, non-circular — never the Path-A load, which is a flat mechanical line by construction.

2. σ_ref — the transparent realized-vol reference

σ_ref is the yardstick the load is measured against. It is a poke-based, time-aware EWMA of Chainlink-tick log-returns — never raw realized σ: σref=max⁡(σshort, σlong, floor).\sigma_{\text{ref}} = \max\bigl(\sigma_{\text{short}},\, \sigma_{\text{long}},\, \text{floor}\bigr). It is published on-chain via VolOracle.sigmaRef(token). Three properties make it the keystone of the moat — see the σ_ref page for the full derivation and calibrated values (short_halflife = 1d, long_halflife = 30d, floor = 0.50):
Because σ_ref is transparent and on-chain, the load charged above it is a well-defined, identifiable quantity — not a back-solved, non-identifiable “implied σ_LP”. This is the property that converts the moat from circular to legible. If σ_ref were hidden, an observed premium could be decomposed into vol-and-load infinitely many ways; with σ_ref public, the load is the only free quantity left, and it is exactly the actor’s choice.
The max(...) is mandatory, not optional engineering: realized vol understates risk right before a regime change. A single fast EWMA would collapse to a deceptively calm number exactly before a crash. Taking the max of a short window, a long window, and a hard floor is the conservative guard. The floor (0.50) sits above observed calm ETH vol (~0.45) on purpose, so the pool overcharges calm months and is net-conservative to depositors.
σ_ref is backward-looking — and the residual forward-looking premium is deliberately left as MM alpha. That residual blindness is the incentive that keeps MMs in the two-sided market, and it is exactly what Signal 2 (the pool-vs-MM spread) extracts: the MM’s forward view, measured against the pool’s backward-looking σ_ref.
σ_ref is solvency-load-bearing for depositors, not for LP no-bad-debt. A wrong/too-low σ_ref makes the pool under-charge load, so premium income stops covering payouts and NAV compresses — junior absorbs it first, senior in the tail. It is NOT load-bearing for the FULL no-bad-debt invariant (I1), which stays structural and oracle-independent: in FULL, collateral = MaxIL ≥ payout regardless of what σ was. Two separate guarantees, never merged.

3. Geometry — the position the LP brings

The third primitive is the bucket. Every behavioral signal is keyed by width × distance-to-edge × duration — the LP’s free choice of what to protect: Geometry is not stored in any event. No SwapPriced / QuoteFilled / SwapCreated field carries the range ticks. Geometry is recovered off-chain by decoding the swap’s tokenId via NPM.positions(tokenId) at the SwapCreated block (range ticks → Pa/Pb), plus expiry − createdAt for duration. That archive positions decode is the one enrichment every geometry-bucketed signal (1, 3, 4) requires.
Why bucketing the load by geometry is informative rather than noisy. MaxIL is the unit that makes positions fungible and the load range-agnostic: premium is quoted as a fraction of MaxIL, so MM/pool return-on-capital is range-independent and there is no adverse selection on range width. That is why an MM quote is per-market (a load + a MaxIL-ratio band + capacity), never per-NFT — the SignedQuote carries no tokenId. With the level normalized out by MaxIL, what remains in the bucketed load is the actor’s view, which is exactly what makes the geometry buckets a clean signal.

4. The on-chain moat events

The 2026-06-05 redeploy landed the two events that record these primitives at every fill, plus the on-chain load decomposition. They are LIVE on-chain now; only the subgraph that indexes them is pending.
1

SwapPriced — the mechanical baseline

The per-fill clearing-price record, emitted on all three create paths.
2

QuoteFilled — the behavioral datum

The single non-circular load datum, emitted on every Path-B fill.
3

loadComponents — the on-chain breakdown

The pure library function that decomposes the pool load into its three skews and feeds SwapPriced.

4.1 SwapPriced — the per-fill clearing-price record

Emitted at the end of _executePathA / _executePathB:
This event records all three primitives atomically — the load decomposition, the σ_ref it was priced against, and (via the tokenId from SwapCreated) the hook for the geometry decode — with no archive eth_call:
uint256 (WAD)
The fair-value baseline the load multiplies. Added so the load can be normalized against fair value — without it, neither the pool’s realized-load fraction nor the MM’s load is normalizable.
uint256 (WAD)
The mechanical Path-A load decomposition (the loadComponents output). On a Path-B fill these fields describe the pool quote the MM beat — the price-to-beat — which is exactly what Signal 2’s spread needs.
uint256 (WAD)
The σ_ref the swap was priced against. Emitted on every fill, so the subgraph backfills the σ_ref series between VolOracle.Poked events (poke is a no-op emitting nothing when dt < minSampleInterval).
bool
true ⇒ the premium hit the MaxIL cap ⇒ the load was truncated ⇒ zero load information. Added because deriving premium == MaxIL off-chain is brittle (rounding); the explicit flag lets every consumer filter cap-bound fills cheaply.
Cap-bound fills carry ZERO load information and MUST be excluded from the load signals (1, 2, 3).
  • Limit — when premium == MaxIL, the number you observe is the cap, not the actor’s load. The load was truncated by the cap.
  • Why — at the cap, the I10 clamp (or the cap itself) has overwritten the actor’s choice; the observed value is geometry, not behavior.
  • Mitigation — filter SwapPriced.cappedAtMaxIL == true at every load-surface consumer. The explicit boolean exists precisely so this filter is cheap and unambiguous.
The 9-field breakdown is assembled into a memory-only SwapPricing struct and emitted via the split-out _emitPriced so the wide event does not inflate the create function’s stack frames.

4.2 QuoteFilled — the single non-circular load datum

Emitted on every Path-B fill in _executePathB:
This is THE single non-circular load datum: the MM’s freely chosen loadBps, attributed to the exact quote and nonce. It is capped only by loadParams.maxLoadBps (I10) — the protocol does not compute it, which is the whole reason it is behavioral rather than circular (see circular vs behavioral). It also closes the pre-redeploy “coarse fill attribution” gap: previously SwapCreated / SwapRouted carried no quoteId / nonce, and isNonceUsed was true on both fill and cancel, so a fill could not be cleanly attributed to a quote.
The subtraction is the point. Signal 2 reports the spread poolLoad − mmLoad = SwapPriced.totalLoadWad − QuoteFilled.loadBps. The mechanical leg (Path A) and the behavioral leg (Path B) arrive in two joined events with no archive call; their difference isolates the MM’s view relative to the transparent baseline, and the subtraction is exactly what removes the circular component. Path A is never sold as a behavioral signal — it is the transparent yardstick the MM load is subtracted from.

4.3 CvammPricing.loadComponents — the on-chain breakdown

The pool load decomposition that feeds SwapPriced is computed on-chain by:
It is called from InflexionCore._pricePathAFromFair. The returned components are pre-clamp — so base + util + disp may exceed total — while total is the I10-clamped sum.
The deployed CvammPricing library is DELEGATECALL-ONLY — a direct eth_call to it reverts.
  • Limit — you cannot eth_call loadComponents against the deployed library address; Solidity guards deployed public-library functions, confirmed on the 2026-06-05 deploy. The library runs on-chain only via the core’s delegatecall during pricing.
  • Why — public library functions are not meant to be called externally; the deployed bytecode rejects a top-level call.
  • Mitigation — the SDK permanently decomposes the two skews using a parity-locked TypeScript port (the SDK loadComponents), asserted byte-equal to the deployed Solidity in the SDK parity test. This is the only permitted duplication: the SDK never reimplements the fairRate Φ-sum (read from the on-chain FairValueOracle), but the load stack is an explicitly deterministic pure transform of public inputs and may be re-evaluated client-side.

4.4 The supporting events the moat relies on

SwapPriced and QuoteFilled are the two new moat events; they sit on top of the existing lifecycle events:

5. How the primitives compose into signals

The three primitives recombine into the five signals by a single template — behavioral leg, measured against the mechanical baseline:
Read it as a table of which primitive each signal leans on: The exact definitions, sourcing, and maturity of each live on the five signals page; the rigorous circularity reasoning — including why inverting fairRate is circular and was deliberately dropped — is on circular vs behavioral.

State and limits

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 pending state — never an error.
Two of the five signals also have halves that can never be on-chain by design: 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. Those latent and dynamic halves live in off-chain engine telemetry, captured from the very first interaction because they are unreconstructable retroactively. See the state-and-limits page for the full inventory, and the access map for how a consumer reaches each primitive.
Depositor capital is NOT guaranteed: the junior tranche is first-loss; the senior tranche is structurally protected from underwriting loss only, not systemic tail.Underwriting capital — whether supplied to the pooled cvAMM vault (Path A) or posted directly by a market maker (Path B) — is at risk:
  • Junior tranche — first-loss. Junior depositors absorb underwriting losses first and buffer the senior tranche.
  • Senior tranche — protected from underwriting loss, exposed to the systemic tail. The senior tranche is structurally shielded from ordinary underwriting loss while junior capital buffers, but it still takes the systemic tail once the junior buffer is exhausted.
No-bad-debt is a guarantee to the LP being paid (see the FULL-mode payout clause), not a guarantee that depositor or MM capital is preserved.