Skip to main content
Inflexion’s access layer is governed by one routing rule and one authority principle. The rule decides which surface answers which question; the authority principle decides who is allowed to compute the answer. The chain is the authority for pricing and settlement; the two off-chain services — the engine (a Path-B quote relayer) and the API (a read-only REST facade) — are deliberately thin convenience and data surfaces that never settle, match, or hold a wallet.
There are exactly three ways a consumer reaches Inflexion’s state, and choosing the wrong one is the most common integration mistake. This page gives you the routing rule, the end-to-end flow that produces the data each layer serves, and the architectural reason each off-chain piece is kept thin.

The three-layer routing rule

The rule is stated verbatim as:
Needs to write, or needs the exact price right now → SDK. Needs history or an aggregate → Subgraph (consumed via the API). Needs frictionless public access with no wallet and no RPC key → API.

SDK

Live on-chain reads + all writes. The only layer that holds a signer and the only one that can answer “what dollar premium will I be charged on this position if I sign in the next block?” Needs an RPC key; can hold a wallet.

Subgraph (via API)

History and aggregates. Internal infrastructure — nobody queries it directly in production except the API. Indexes the on-chain moat events into the entities the five behavioral signals aggregate over.

API

Cached, public, read-only. No wallet, no RPC key, no signer for the consumer. A thin node:http facade composing the subgraph, a small set of live RPC reads (routed through the SDK), and the engine’s telemetry sinks.

Why three layers and not one

Each layer exists because the other two cannot do its job without giving something up:
The pricing implementation exists in exactly one place per concern. The fair rate (the Φ-sum) is computed only on-chain by the Stylus FairValueOracle and read through the SDK. The deterministic load stack is computed on-chain in pricing and re-evaluated client-side only via the SDK’s parity-locked CvammPricing TS port (the deployed library is delegatecall-only, so a direct eth_call reverts). The API never re-derives the fair rate or the load stack — it forwards the SDK’s row. One implementation per concern is what keeps the access layer honest.

The two off-chain services

The access layer wraps the on-chain core — InflexionCore, the Stylus FairValueOracle, the cvAMM ConvexityVault — with two small Node/TS services. Both are framework-free (node:http), offline-test-friendly, and intentionally thin.

@inflexion/engine — the Path-B relayer

A thin off-chain relayer for the optional market-maker rail. Accepts EIP-712 signed quotes over WebSocket, verifies signature + light freshness, serves the cheapest live quote, and captures the two day-one telemetry signals. Does not match or settle — the chain does.

@inflexion/api — the public REST facade

A public, read-only, cached facade — no wallet, no signer, no RPC key for consumers. Composes three independently-degrading backings: the subgraph, live RPC reads via the SDK, and the engine’s telemetry sinks.

Where the chain stays the authority

Pushing matching or settlement off-chain would reintroduce trust — and would break the no-bad-debt guarantee. So the relayer is structurally incapable of doing either:
  • It cannot compute a dollar premium. The final premium is position-specific and derived on-chain at createSwapRouted as FairPremium⋅(1+loadBps)\text{FairPremium} \cdot (1 + \text{loadBps}). The relayer returns the signed quote + its loadBps, not a dollar figure — it would need the LP’s exact geometry and an RPC read of FairPremium to do otherwise.
  • It applies only a light, non-authoritative freshness gate. Its skew window (maxValiditySkewS = 60s) is deliberately looser than the on-chain validity window of [5, 15] s. The chain re-checks everything; the relayer just drops obvious junk so the store stays clean.
  • It holds no wallet and never signs. It verifies an MM’s EIP-712 signature; it never produces one.
Limit — the engine is the only place latent demand and lost quotes are ever recorded; if the sinks are not configured from the first deploy, that data is gone forever.
  • Why. Invariant I7 guarantees an unchosen quote touches no nonce and no capacity, so an LP who priced but did not buy, and an MM quote that lost or was withdrawn, leave no on-chain trace by design. Putting unfilled interest on-chain would both cost gas and break I7.
  • Mitigation. The engine writes two append-only JSONL telemetry sinks — DEMAND_LOG (Signal 4 latent half) and COMPETITION_LOG (Signal 2 dynamic half) — from the very first interaction. The writer is best-effort and never throws: a full disk or missing sink never breaks a quote ack, a preview, or a /quote request. Set DEMAND_LOG and COMPETITION_LOG on the engine from deploy day. See the engine and telemetry.

Why Path A needs no engine at all

The engine exists only so a sophisticated MM can compete on price against the pool. Path A — the cvAMM pool — is always-on and signature-free; a pooled vault underwrites with no relayer in the loop. createSwapRouted then hands the LP the cheaper of {pool, MM quote}. The engine is pure upside: if no MM is connected, Path A still clears every position, and an expired, over-band, or under-collateralized MM quote causes routing to fall back to the pool, never revert. See routing.

End-to-end: MM → engine → chain → API

This is the flow that produces the state each layer serves. It is the canonical sequence followed end to end.
1

MM streams a firm quote to the engine (WebSocket)

The market maker streams { type: 'quote', quote, signature } over WS. The relayer verifies the EIP-712 signature + the light freshness gate, stores it as the cheapest-per-market candidate, and logs it to COMPETITION_LOG (a winner if accepted, a loser if rejected — both are logged). The quote is per-market, never per-NFT: it carries a loadBps, a [minMaxILRatioBps, maxMaxILRatioBps] band, and a capacity ceiling, but no tokenId. This is load-bearing — MaxIL makes positions fungible to an underwriter within a market.
2

LP previews via the SDK

LpClient.previewPremium reads the live pool-load surface (RPC, or the API’s /pool/load-surface) and the cheapest MM load from the engine’s GET /quote, then best-effort POSTs /telemetry/preview (logged to DEMAND_LOG, filled: false). The preview ping carries only bucketed geometry + marketId (no PII, no tokenId, no addresses).
3

LP buys on-chain — the chain re-derives everything

The LP calls createSwapRouted on InflexionCore. The chain re-derives FairPremium (the Φ-sum from the Stylus FairValueOracle), re-checks the quote’s [5, 15] s validity window, the price band (invariant I9 / Fork 2), and the bitmap nonce (I7), then routes to the cheaper of {cvAMM pool, MM quote}. An invalid quote falls back to Path A, never reverts. Only the executed rail mutates nonce, capacity, and the collateral lock.
4

Settlement pays the LP from the chosen underwriter

settle() pays the LP min⁡(realized_IL,MaxIL)\min(\text{realized\_IL}, \text{MaxIL}) trustlessly from the chosen underwriter’s collateral. The 2026-06-05 lifecycle demonstrated both rails: Path A (cvAMM, swap #1, premium $9.70 = 0.58% of MaxIL, payout $148.64) and Path B (MM beat the pool, swap #2, MM load 1000 bps, payout $245.66 from MM collateral).
5

The API exposes the microstructure

Live surfaces are served now via RPC (through the SDK); the off-chain telemetry halves are served now from the JSONL sinks; history and aggregate surfaces are served live from the subgraph via the API. The subgraph has indexed the on-chain SwapPriced / QuoteFilled events from the redeploy block, joining the structural moat halves to the dynamic halves already captured. See the five signals.
The two on-chain events that the subgraph indexes for the moat both shipped in the 2026-06-05 redeploy and are live on-chain now, and the subgraph indexes them live:

What is intentionally thin — and why

Thinness is a design constraint, not an unfinished state. Each service is kept small so the chain stays the only authority and so both degrade gracefully under absence.
The relayer’s entire job is three things: verify + store the latest signed quote per (market, MM), serve the cheapest live quote over GET /quote, and capture the two day-one telemetry signals. Matching and settlement are the chain’s — pushing them off-chain would reintroduce trust and break the no-bad-debt guarantee. The relayer returns a load, not a dollar premium, because the premium is position-specific and computed on-chain.
Live RPC reads in the API reuse @inflexion/sdk DataClient.getCurrentLoadSurface and DepositorClient.getVaultState. The Φ-sum fair rate is computed in exactly one place (the Stylus FairValueOracle, surfaced through the SDK); the API only forwards it. This is a hard rule — duplicating the pricing math in the API would create a second source of truth.
Consumers need no RPC key and no signer. Non-GET requests return 405 { available:false, reason:'method-not-allowed', detail:'read-only API; GET only' }. The whole stack is fully testable offline — mock subgraph, injected fetch, injected telemetry reader, pre-built SDK clients.
The store is in-memory (marketId → mm → latest quote) with an optional append-only JSONL log; multi-MM works because the store keys per MM, but there is no auth, no rate-limit, and no persistence beyond the JSONL log. The definitive Stylus ≡ Solidity ≡ off-chain digest cross-check is an integration test added with the SDK. Production hardening (auth, rate-limit, durable store) is a bounded follow-up — the architecture does not change, only the operational envelope.

Everything degrades to a typed value, never an exception

Both services treat absence as a first-class, typed state — not an error path. This is what lets a UI render a “pending” or “degraded” panel instead of catching an exception.
  • The API response envelope is a discriminated union that never throws:
  • PendingReason is a closed enum: subgraph-not-deployed, subgraph-unreachable, rich-events-pre-redeploy, telemetry-sink-absent, rpc-unavailable. Each one names precisely what unblocks the surface.
  • Subgraph-backed routes embed the exact query so the surface is self-documenting; if a backing is ever absent, the pending body still names precisely what data the route serves. A consumer can read exactly what data appears.
  • A last-resort 500 guard wraps every handler: a stray bug returns 500 { available:false, reason:'internal' } rather than crashing the process.
Live — the subgraph is deployed and indexing, so every history/aggregate ([SG]) surface serves real data now via the API.The subgraph is deployed on The Graph Studio (slug inflexion-arb-sepolia) and has indexed the on-chain moat events (SwapPriced / QuoteFilled, both live on-chain) from the redeploy block 274081134 — the subgraph startBlock, where the on-chain moat dataset begins. The hosted API composes it alongside the live RPC surfaces (current load surface, vault state, pricing preview) and the telemetry halves (DEMAND_LOG, COMPETITION_LOG). Each [SG] route runs its embedded GraphQL against the subgraph; if SUBGRAPH_URL were ever unset, the same route degrades to a typed { available:false, reason:'subgraph-not-deployed', query:… } body rather than erroring. See status & caveats and the moat state.

Which layer serves which surface (live status)

A quick map of what is reachable today, by layer. Backing key: [RPC] live via the SDK, [SG] subgraph (deployed and indexing), [TEL] engine telemetry sink.
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.

The claims, stated correctly

The access layer carries two separate, never-merged standing disclosures. The API attaches Claim A to every payout/swap/MM surface and Claim B to every NAV/yield/pool surface; they are never combined into one statement. LP no-bad-debt (Claim A) and depositor/MM capital risk (Claim B) are distinct claims.
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.
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

The engine

The Path-B relayer in depth: the SignedQuote, the WS protocol, the freshness gate, and the day-one telemetry sinks.

API overview

The response envelope, the closed PendingReason enum, caching, and graceful degradation.

API endpoints

The full path · query · backing · returns reference for every route.

The subgraph

The internal indexer: the moat entities, the embedded queries, and the marketId-parity note.

SDK overview

The typed access layer — the only signer-holding surface, its five clients, and degradation model.

The data moat

The five non-circular behavioral signals and how the access layer reaches each one.
For the live contract addresses, see the deployments reference. Network: Arbitrum Sepolia · chainId 421614 · deploy block 274081134 (subgraph startBlock) · deployed 2026-06-05

Core protocol

Stylus & libraries

Oracles

Uniswap

Demo tokens / pool / LP

Lifecycle swaps

Live create→settle lifecycle (2026-06-05, short 300s fee-500 market 0xb8bbd684f213d5833886ade7b531a6949d85522249881a2b5d46a5cc76e439c2):
  • Path A (cvAMM convexity) — swapId 1, LP tokenId 3220 · V0 $270,531.28 · MaxIL $1,669.24 · premium $9.70 (0.58% of MaxIL) · settled payout $148.64 from ConvexityVault.
  • Path B (MM routing) — swapId 2, LP tokenId 3221 · MM beat cvAMM ($8.93 vs $13.80) · MaxIL $3,215.65 · MM collateral locked $3,215.65 · settled payout $245.66 from MM collateral.