Skip to main content
The SDK is not neutral plumbing. It carries the protocol’s load-bearing claims in its types and exported constants, so that any UI, bot, or integration that reads from it inherits the correct framing for free — and so that getting the framing wrong takes deliberate effort, not just inattention. This page is the “say it right” reference. Every rule below is something @inflexion/sdk encodes structurally. If you are building on Inflexion, treat these five rules as non-negotiable in your copy and your tooltips.
The framing rules are also captured in code as a self-contained guardrail block: the no-bad-debt clause, claims A and B (exported verbatim as LP_CLAIM_A / DEPOSITOR_CLAIM_B), and the hedge caveat. When in doubt, quote the exported constant rather than paraphrasing.

The five rules at a glance

1. In-range convexity hedge

Not “IL insurance”. Entry requires Pa≤P0≤PbP_a \le P_0 \le P_b; payout is capped at MaxIL. The cap is load-bearing.

2. Qualified no-bad-debt (Claim A)

Exact only under the full clause. Never stated unqualified. Lives on the LP surface only.

3. Capital is not guaranteed (Claim B)

Both tranches. Junior first-loss; senior protected from underwriting loss only. Never merged with Claim A.

4. Quotes are per-market

MaxIL is the unit of risk → positions are fungible. A SignedQuote carries no tokenId.

5. The Phi-sum is read, never reimplemented

FairValue is an on-chain closed form. Only the deterministic load stack is mirrored off-chain, parity-locked.

Rule 1 — In-range convexity hedge, not “IL insurance”

Inflexion lets an LP transfer the in-range impermanent-loss risk of a specific position. Two structural facts make this a convexity hedge rather than insurance, and the SDK enforces both:
1

Entry requires Pa ≤ P0 ≤ Pb

Out-of-range positions are rejected at creation. The SDK computes inRange = sqrtP0 >= sqrtPa && sqrtP0 <= sqrtPb in resolveGeometry and surfaces it on every LpGeometry and EligiblePosition. This mirrors the on-chain createSwap in-range gate — there is no way to protect a position that is already out of range.
2

Payout is min(realized_IL, MaxIL)

The settlement payoff is the capped IL, not the raw IL. The cap is applied in getPayoffCurve and in ilToDate, where capHit = payout < il tells you whether the cap bound.
The capped payoff is what makes this convexity: the LP is long gamma / long vega on the in-range region, and the protocol is short it. The cap is the unit of risk as well as the safety mechanism — see Rule 4. The realized-IL payoff, with the cap that is load-bearing for everything downstream: payout  =  min⁡ ⁣( realized_IL,  MaxIL ),realized_IL  =  {Vhold−Vlpif Vhold>Vlp0otherwise\text{payout} \;=\; \min\!\bigl(\,\text{realized\_IL},\; \text{MaxIL}\,\bigr), \qquad \text{realized\_IL} \;=\; \begin{cases} V_{\text{hold}} - V_{\text{lp}} & \text{if } V_{\text{hold}} > V_{\text{lp}} \\[2pt] 0 & \text{otherwise} \end{cases} The right branch is invariant I3 (non-negativity / no underflow) and I4 (the LP never profits from the swap): if the LP would have done better holding LP than holding the underlying, payout is exactly zero. The SDK never produces a negative payout; the Priceable<...> envelope returns il and payout as unsigned bigint.
Do not call it “IL insurance”. “Insurance” implies an uncapped indemnity against any loss. Inflexion is an in-range convexity hedge: the protection only exists while Pa≤P0≤PbP_a \le P_0 \le P_b, and the payout is capped at MaxIL. The cap is not a deductible — it is the mechanism that makes the no-bad-debt guarantee exact (see Rule 2). Removing or mis-describing the cap breaks the framing of the entire protocol.
See the in-range convexity hedge and the IL formula for the full derivation.

Rule 2 — Qualified no-bad-debt (Claim A)

The LP-facing claim is: LPs are always paid — no bad debt in FULL mode (invariant I1). The SDK carries this on the LP surface as noBadDebtFull: true on ProtectionStatus and exports the full sentence verbatim as LP_CLAIM_A. But the claim is exact only under its full qualifying clause. State it any other way and it is wrong.
LPs are always paid with no bad debt in FULL mode — qualified: capped payoff + solvent USDC + oracle/settlement liveness.The no-bad-debt guarantee is exact only under the full clause:
  • FULL collateralization — the underwriter has posted exactly MaxIL per swap, locked for the life of the position.
  • Capped payoff — the payout is min(realized_IL, MaxIL). The cap is load-bearing: it is what makes payout ≤ collateral hold by construction.
  • Solvent USDC — the collateral numeraire (dUSDC) holds its peg and remains redeemable.
  • Oracle / settlement liveness — the price oracle stays live and settle() runs within the protocol’s settlement window.
  • No rehypothecation breach — locked collateral stays instantly liquid; it is never routed to utilization-gated venues.
Stated without these qualifiers, the guarantee is not correct.
The clause, spelled out — every term is load-bearing:
Never state no-bad-debt unqualified. Writing “Inflexion can’t produce bad debt” or “LPs can’t lose” without the clause is a defect. It is exact only under FULL collateralization + capped payoff + solvent USDC + oracle/settlement liveness + no rehypothecation breach. Use the <ClaimA /> snippet rather than re-wording, and quote LP_CLAIM_A if you need the canonical sentence in code.
Claim A is about the LP being paid. It says nothing about depositor or MM capital — that is a separate claim (Rule 3). The SDK keeps them on separate surfaces precisely so they cannot be accidentally merged.
See no-bad-debt, the no-bad-debt math, and the invariants.

Rule 3 — Capital is not guaranteed (Claim B)

The underwriter-facing claim is the mirror image of Claim A, and it must never be softened. The SDK stamps capitalNotGuaranteed: true on every VaultState and DepositorPosition result and exports the full sentence as DEPOSITOR_CLAIM_B. It is structurally impossible to read vault state from the SDK without the flag attached.
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.

Both tranches carry risk — differently

The protocol underwrites with a two-tranche waterfall. Neither tranche is risk-free:
Junior is the first-loss buffer: it captures most of the premium and absorbs underwriting losses first. The on-chain withdraw enforces the JuniorBelowLocked gate so junior cannot drain below the locked amount and break senior protection. A junior withdrawal that would cross the locked floor reverts and surfaces as WriteResult { status: 'error' }, not a silent partial.
The structural fact, as an invariant: totalLocked  ≤  juniorAssets(junior absorbs underwriting loss before senior)\text{totalLocked} \;\le\; \text{juniorAssets} \qquad\text{(junior absorbs underwriting loss before senior)}
Never imply either tranche is risk-free. Junior is first-loss; senior is protected from underwriting loss while junior buffers, but takes the systemic tail. Render the <ClaimB /> disclosure on every depositor-facing surface, and do not borrow Claim A’s “no bad debt” language to describe depositor capital — that conflates two distinct claims.
See tranches, the vaults, and the risk disclosure.

Rule 4 — An MM quote is per-MARKET, never per-NFT

This is the rule integrators most often get wrong, because intuition says “I’m pricing this position.” You are not. MaxIL is both the load-bearing payout cap and the unit of risk:
  • MaxIL is pure geometry — a function of (Pa,Pb,P0)(P_a, P_b, P_0), frozen at creation.
  • It is L-independent in the fair-rate sense: the liquidity LL scales the dollar amount but not the fair rate.
Because the unit of risk is geometry, not the specific NFT, positions are fungible to an underwriter within a market. So a market-maker quotes a market, not a token: MM quote  =  loadBps⏟a load over on-chain FairPremium  +  [ minMaxILRatioBps, maxMaxILRatioBps ]⏟a MaxIL-ratio band  +  maxNotionalV0⏟capacity\text{MM quote} \;=\; \underbrace{\text{loadBps}}_{\text{a load over on-chain FairPremium}} \;+\; \underbrace{[\,\text{minMaxILRatioBps},\, \text{maxMaxILRatioBps}\,]}_{\text{a MaxIL-ratio band}} \;+\; \underbrace{\text{maxNotionalV0}}_{\text{capacity}} The proof that this is per-market and not per-NFT is in the wire format: the SignedQuote struct carries no tokenId. Its fields are mm, marketId, loadBps, minMaxILRatioBps, maxMaxILRatioBps, quotePrice, priceBandBps, model, partialRatioBps, maxNotionalV0, validUntil, quoteId, nonce — geometry-independent, every one. The SDK leans into this: the first-class streamable signal is getPoolLoadToBeat / streamPoolLoadToBeat — the pure pool load an MM must undercut, computed without any position geometry:
A quote’s [minMaxILRatioBps, maxMaxILRatioBps] band says “I will underwrite any position in this market whose MaxIL ratio falls in this band, up to maxNotionalV0 of notional.” One signed quote can fill many different NFTs. This is the fungibility that makes a competitive MM market possible at all.
Do not describe an MM quote as pricing a specific position. It prices a market: a load, a MaxIL-ratio band, and a capacity. If your UI shows “MM quote for token #3218”, that is wrong framing — show “MM quote for market <marketId>, valid for any position in the MaxIL band.” The absence of tokenId in SignedQuote is the proof.
See MaxIL, the MaxIL derivation, Path B, and quote signing.

Rule 5 — The Phi-sum is read on-chain, never reimplemented

FairValue is an exact closed-form Φ\Phi-sum — the Stylus FairValueOracle (fairPremium / fairRate). It is the protocol’s single source of pricing truth, and the SDK reads it; it never recomputes it off-chain. The on-chain read, in the SDK’s hot path:

The one permitted duplication — and why it is permitted

There is exactly one piece of pricing math the SDK mirrors off-chain: the deterministic, pure load stack (baseLoad + utilSkew + dispSkew, clamped to maxLoadBps). It lives in the cvamm namespace and is parity-locked byte-equal to the deployed CvammPricing Solidity by a dedicated parity test. Why mirror it at all? Because the deployed CvammPricing library is delegatecall-only: a direct eth_call to libs.cvammPricing reverts (Solidity guards deployed libraries — confirmed on the 2026-06-05 deploy). The library runs on-chain only via the core’s delegatecall during pricing, so there is no callable on-chain entrypoint to read the load from. The parity-locked TS port is the only way to preview the load without sending a transaction. The split is precise: premium⏟Path A  =  fairPremium⏟read on-chain (Φ-sum)×(1+totalLoad⏟mirrored off-chain (load stack)),capped at MaxIL\underbrace{\text{premium}}_{\text{Path A}} \;=\; \underbrace{\text{fairPremium}}_{\text{read on-chain (Φ-sum)}} \times \bigl(1 + \underbrace{\text{totalLoad}}_{\text{mirrored off-chain (load stack)}}\bigr), \quad \text{capped at MaxIL}
Never reimplement the Φ\Phi-sum off-chain. Do not “approximate fair value” in JS, cache a stale fairRate, or invert it to back out an implied vol for pricing. The FairValueOracle is the only authority; the SDK reads it fresh every preview. The only math you may evaluate off-chain is the deterministic load stack — and only via the parity-locked cvamm port, never a hand-rolled copy. If cvamm and the deployed library ever disagree, the parity test fails the build.
Inverting fairRate to infer implied volatility is also a circular signal — it would just recover the σ_ref the oracle was fed. It is deliberately excluded from the data-moat signals for exactly that reason. See circular vs behavioral.
See the three pillars, the Phi-sum, the pricing stack, and the cvAMM port.

One more guardrail: the hedge caveat

If you surface the SDK’s read-only analytics (GreeksEngine / HedgeSuggester), there is a sixth framing rule you must carry verbatim. Every HedgeSuggestion attaches HEDGE_CAVEAT:
  • The hedge is approximate relative to the fixed-maturity IL claim (mismatched expiry, funding, discretization).
  • It is the MM’s own residual-risk choice, not a protocol requirement.
  • It is not relied upon for pool solvency — invariant I1 (no bad debt, FULL) is structural and oracle-independent, fully collateralized at MaxIL regardless of whether the MM hedges at all.
executeOnPanoptic always returns executed: false — the SDK never submits a hedge tx. It is analytics, not execution.
Do not imply the hedge is what keeps the pool solvent. Solvency comes from full collateralization at MaxIL (I1), which holds whether or not any MM hedges. The hedge is the MM’s private business decision about its own residual risk. Attach HEDGE_CAVEAT verbatim to any suggestion you display.
See greeks & hedge and the trust model.

Quick checklist for integrators

Say “in-range convexity hedge”, not “IL insurance”. State the payout as min⁡(realized_IL,MaxIL)\min(\text{realized\_IL}, \text{MaxIL}) and note the cap. Mention the entry gate Pa≤P0≤PbP_a \le P_0 \le P_b if there is room.
Always attach the full clause via <ClaimA /> (FULL + capped payoff + solvent USDC + oracle/settlement liveness + no rehypothecation breach). Never the bare phrase. This is the LP claim — keep it off depositor surfaces.
Render <ClaimB />. Both tranches carry risk: junior first-loss, senior protected from underwriting loss only and exposed to the systemic tail. Never “risk-free” or “guaranteed”. Do not reuse Claim A’s no-bad-debt language here.
A quote is per-market: load + MaxIL-ratio band + capacity. The SignedQuote has no tokenId. Never present a quote as pricing one specific NFT.
Read fairPremium / fairRate from the on-chain FairValueOracle fresh. Only the load stack may be evaluated off-chain, and only via the parity-locked cvamm port. Never reimplement or invert the Φ\Phi-sum.
History surfaces (load-surface history, quote competition, NAV, net gamma) are subgraph-pending. The SDK returns a typed ApiPending envelope naming the future route — render the pending state, never an error, never fabricated numbers.
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.
For the canonical sentences in code, import them rather than retyping: LP_CLAIM_A, DEPOSITOR_CLAIM_B, and HEDGE_CAVEAT are all exported from @inflexion/sdk.