Skip to main content
Two contracts run through the whole SDK. A read never throws — it returns a typed degraded envelope you must narrow before you can touch the data. A write never crashes at construction — without a signer it either throws a clear error on call or returns a deferred result carrying the unsigned tx. This page is the complete catalogue of those envelopes, the live-today vs degradation cheat-sheet, and the approvals/spenders table — the three things a UI engineer needs to render Inflexion safely, including when a backing (subgraph URL / RPC / telemetry) is absent.
A degraded read is a value, not a thrown error. That single decision is what lets the app render a typed “oracle degraded” or “served from <route>” state instead of wrapping every call in a try/catch. The type system enforces it: every fallible read returns a discriminated union, and the compiler will not let you read the payload until you check the discriminant.

The five envelopes

There are exactly five degradation shapes in the SDK. Three are read envelopes (Degraded<T>, Priceable<T>, ApiPending), one is a read precision flag (NonceStatus), and one is the write-result union (WriteResult). Everything fallible the SDK returns is one of these.

Degraded<T>

Generic read envelope: success or a { available: false, reason }. Vault state, position, regime, surface σ_ref.

Priceable<T>

Anything that needs the live price P0P_0. Success or NotPriceable { priceable: false, reason, detail? }.

ApiPending

History/aggregate surfaces when no backing is configured. Names the route + the exact query. Served live today via the hosted API.

NonceStatus

Not pass/fail — a precision flag: 'coarse' | 'precise'. MM fill attribution is coarse on-chain.

WriteResult

The DepositorClient write union: { tx, txHash?, status: 'sent' | 'deferred-no-wallet' | 'error', detail? }. LP/MM writes throw a clear error instead of deferring.

Quick reference


Degraded<T> — generic read degradation

The general-purpose envelope for reads that can fail for an environmental reason (no RPC, an uninitialised vol oracle) rather than a price-specific one. DepositorClient.getVaultState, getPosition, getRegime, and DataClient.getSurfaceSigmaRef all return Degraded<T>.
On Degraded<VaultState>, the success branch carries capitalNotGuaranteed: true — the SDK attaches Claim B to every depositor state read. Render the tranche risk disclosure wherever you surface vault state.

Priceable<T> — anything that needs the live price

Distinct from Degraded<T> because the dominant failure is the live price P0P_0 being unavailable. Every read that derives P0P_0 catches an OracleManager.getPrice revert (stale feed, sequencer down) and returns NotPriceable { reason: 'oracle-degraded' } rather than throwing. This covers lp.resolveGeometry, previewPremium, getPayoffCurve, and the nested ilToDate inside getProtectionStatus.
Envelopes nest. getProtectionStatus returns ProtectionStatus | { available: false, reason }, and its ilToDate field is itself a Priceable<{ il, payout, capHit }>. So a swap can be known (available: true) while its mark-to-market IL is not priceable this block (oracle degraded) — and the UI shows the position with a “mark unavailable” badge instead of failing the whole card.

DegradeReason — the closed union

Every reason on a Priceable/Degraded/ApiPending failure is drawn from one closed union, so the UI can exhaustively switch on it:

ApiPending — the SDK that never fabricates history

History/aggregate surfaces are served live from the subgraph via the hosted API (/), backed by the deployed subgraph (slug inflexion-arb-sepolia) which has indexed on-chain history since the redeploy block. ApiPending is the graceful-degradation fallback: if no backing is configured (e.g. SUBGRAPH_URL is unset), each DataClient history method returns this typed envelope rather than inventing a number — it names the route and the exact query that would be sent, so the UI can render “served from <route>” with zero re-derivation when a backing is present. The SDK never fabricates history.
History/aggregate surfaces are live today — served from the subgraph via the hosted API.The deployed subgraph (slug inflexion-arb-sepolia) has indexed on-chain history since the redeploy block, so all five data-moat history signals resolve to real data through the API. getCurrentLoadSurface and getSurfaceSigmaRef hit live RPC directly. getDemandRequests is the one inherently off-chain signal: it is served from engine telemetry, not the chain, because latent demand never touches the chain by invariant I7. If no backing is configured (e.g. SUBGRAPH_URL is unset), the history methods degrade to a typed ApiPending envelope naming their route — they never throw.

NonceStatus — coarse vs precise fill attribution

Not a pass/fail envelope — it always returns — but it carries a precision flag that degrades, and that distinction is load-bearing for MMs.
MM fill attribution is COARSE on-chain — and that is structural, not a bug.
  • Limit. mm.isQuoteFilled(mm, nonce) returns precision: 'coarse'.
  • Why. The bitmap nonce isNonceUsed(mm, nonce) is set on both a fill and a cancel, and SwapCreated / SwapRouted carry no quoteId/nonce — so a spent bit cannot, on-chain alone, be told apart as filled-vs-cancelled.
  • Mitigation. The precise path is the QuoteFilled event (swapId, mm, quoteId, nonce, loadBps) emitted since the 2026-06-05 deploy; it is now indexed by the live subgraph and queryable as precision: 'precise'. The coarse on-chain path remains available too — watchFills polls SwapCreated logs filtered by mm, and capacityRemaining reads the exact on-chain consumedNotional (invariant I7) for the number that actually matters — remaining capacity.

WriteResult — writes fail clearly, never silently

The factory always constructs, even with no signer — makeWalletClient returns undefined rather than crashing. What happens when you then call a write is deliberately different per surface, because the right failure mode differs by intent.
deposit, requestWithdrawal, and withdraw return a WriteResult. Without a signer the status is 'deferred-no-wallet' and tx carries the unsigned calldata for an external signer — the right shape for server-side and custody flows where the SDK builds calldata that something else signs. An on-chain revert (e.g. the JuniorBelowLocked gate on withdraw) surfaces as status: 'error', never a thrown exception.
Most write methods have a build* sibling that returns raw { to, data, value } calldata directly, for full external-signing control regardless of wallet state: depositor.buildDeposit / buildApprove, mm.buildCancelNoncesTx. See guardrails for the full write-safety model.

Live-RPC vs subgraph-backed — the cheat-sheet

The SDK is fully usable against the live deployment, and the subgraph is deployed and indexing too. The line is between live-RPC surfaces (data straight from the chain) and history/aggregate surfaces served from the subgraph via the hosted API — which degrade to a typed pending state only if no backing is configured.
The subgraph is deployed and indexing — history, NAV, precise fill attribution, and full fee accounting are live, served via the hosted API.The subgraph (slug inflexion-arb-sepolia) has indexed on-chain history since the redeploy block, so these surfaces resolve to real data with no consumer-side changes. Graceful degradation remains a design property: if a backing is absent (e.g. SUBGRAPH_URL is unset), each affected surface degrades to a typed envelope — ApiPending (history), NonceStatus { precision: 'coarse' } (fills), or an under-stating-but-honest read (getClaimableFees) — and the UI renders the pending state; nothing throws.
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.

Approvals & spenders — the one-glance table

Reads need no approvals. For writes, the spender differs per surface — and getting it right is the difference between a successful tx and a wasted approval to the wrong contract.
The LP and depositor spenders are different contracts — do not cross them. buyProtection approves dUSDC (and the position NFT) to InflexionCore; deposit approves dUSDC to the ConvexityVault; MM collateral goes to the UnderwriterVault. Approving the wrong contract leaves the real call under-allowanced and reverting. The SDK’s auto* flags wire the correct spender for you — prefer them over hand-rolled approvals.
All collateral and premium flows are denominated in dUSDC, 6 decimals (the numéraire — 1 dUSDC = 1_000_000n). maxPremium, amount, V0, maxIL, and every collateral figure are 6-dec raw bigints. See decimals & units and addresses & units.

The depositor claim, stated correctly

Because the live load surface, NAV history, and vault-state reads on this page all touch depositor capital, the SDK attaches Claim B to every depositor read. It is never merged with Claim A (LP no-bad-debt).
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.

Where to go next

Guardrails

The write-safety model in full — build* calldata, deferred results, and the never-merge claim discipline.

Data client

The live-RPC surface and the five behavioral signals of the data moat, served via the API.

Getting started

Construction, the full config table, and the RPC/key resolution order that drives no-rpc.

Subgraph

What the live subgraph indexes, and which envelopes resolve from coarse/pending to precise.

Invariants

I7 (capacity authority) and the settlement-liveness fork behind oracle-degraded.

Deployments

Live Arbitrum Sepolia addresses — never hardcode; import from the SDK.