LpClient is the LP-facing surface of @inflexion/sdk: it prices, buys,
inspects, and settles in-range IL protection on a
specific Uniswap v3 position. It is the surface that carries Claim A — LPs are always
paid min(realized_IL, MaxIL) from the underwriter’s collateral, with no bad debt in FULL
mode — and the only one that holds a signer for LP writes.
Access it as sdk.lp from the
one-call factory. It is the only surface wired with
engineBaseUrl / telemetryUrl / fetchImpl, because it is the only one that talks to
the Path-B quote engine and emits the latent-demand telemetry ping.
The read contract: graceful degradation
Every read that needs the live spot priceP0 wraps the OracleManager.getPrice call.
If that oracle is stale or the L2 sequencer is reported down, the manager reverts, and
the SDK catches it and returns a typed NotPriceable envelope rather than throwing:
try/catch:
walletClient,
buyProtection / settle / poke throw a clear error on call (not at construction),
so a read-only SDK still constructs and prices fine. See the full
envelope taxonomy in SDK degradation.
Method reference
Decimals are load-bearing. dUSDC is the 6-decimal numéraire — every
maxPremium, premium, V0, and maxIL is a 6-dec raw bigint (1 dUSDC = 1_000_000n).
dWETH is 18-dec, Chainlink feeds are 8-dec, and ratios (aWad, bWad, σ_ref, loads) are
WAD = 1e18. See decimals & units.getLoadParams
Reads and caches the protocol’s load-stack parameters from
InflexionCore.loadParams. These feed the client-side TS-port load
math (regime bands, util/disp curves, maxLoadBps = invariant I10).
None.
LoadParams
The on-chain load parameters. Cached after the first read; throws only if there is no RPC
client and the cache is empty.
resolveGeometry
Resolves a position NFT + a market into the full settlement geometry — the heart of
pricing. It reads the NFT’s ticks, derives sqrtPaX96 / sqrtPbX96 from TickMath,
reads the live sqrtP0X96 (from the oracle-derived price), converts to the WAD ratios
a/b, computes entry amounts and MaxIL, and stamps the inRange gate.
bigint
required
The Uniswap v3 position NFT id.
Priceable<{ geometry: LpGeometry }>
On success,
{ priceable: true, geometry }. On failure,
{ priceable: false, reason } with reason ∈ 'market-unknown' | 'position-unknown' | 'oracle-degraded'.listEligiblePositions
Enumerates an owner’s Uniswap v3 NFTs and prices each one across the requested durations.
It reads balanceOf / tokenOfOwnerByIndex / positions from the NPM, then runs
resolveGeometry per (position × duration) pair.
0x${string}
required
The wallet whose positions to enumerate.
number[]
Durations (seconds) to price each position against. Defaults to the market grid’s
durations.
EligiblePosition[]
One row per (position × duration). Each row carries the resolved geometry plus eligibility
flags. Returns an empty array if the NPM is unreachable. Per row,
inRange is
undefined when the oracle is degraded for that price (the position can’t be priced this
block).previewPremium
The load-bearing method: “what dollar premium will I be charged on THIS position if I
sign in the next block?” It is the best-of {pool, MM} quote, both capped at the same
MaxIL.
bigint
required
0x${string}
required
boolean
If
true, sends a VolOracle.poke(token) write before reading to refresh the σ_ref EWMA
(otherwise pure read).string
Override the Path-B quote engine base for this call.
Priceable<PreviewResult>
On success the full pricing breakdown; on oracle degradation,
NotPriceable.1
Read the on-chain fair value
FairValueOracle.fairPremium(token, aWad, bWad, durationSeconds, maxIL) →
(premium, fairRateWad, sigmaRefWad). The Φ-sum is never
reimplemented off-chain.2
Read pool inventory + params
ConvexityVault.inventory() → (total, locked, free, util, conc) and loadParams().3
Finish the load stack client-side (the single permitted duplication)
totalLoad = cvamm.totalLoadWad(sigmaRef, util, conc, params), then
premiumA = cvamm.premiumFromLoad(fairPremium, totalLoad) capped at MaxIL — byte-equal
to _pricePathAFromFair on-chain because the deployed CvammPricing lib is
delegatecall-only.4
Fetch the best MM quote (best-effort)
GET ${engineBaseUrl}/quote?marketId=… for the cheapest live signed quote;
premiumB = cvamm.pathBPremium(fairPremium, loadBps, maxIL) capped at the same MaxIL. An
unreachable engine leaves premiumB undefined and routes Path A — never an error.5
Pick the cheaper, then fire telemetry
best/path = min(premiumA, premiumB). A fire-and-forget telemetry
ping (latent-demand Signal 4) is sent — never awaited, never throws.getPayoffCurve
Computes the LP’s settlement payoff across a grid of terminal prices, with the cap
applied. Drives the payoff chart in the protect flow.
bigint
required
0x${string}
required
number
Number of grid points across the sqrt-price range. Higher = smoother curve.
Priceable<PayoffCurve>
On success, the curve of
(price, il, payout) where payout = min(il, MaxIL), evaluated
via ILMath.computeIL over a sqrt-price grid. NotPriceable if the
oracle is degraded.buyProtection
The LP write. Sends approvals (by default) and fires the chosen create call on
InflexionCore.
bigint
required
The position NFT to protect.
0x${string}
required
Required for the Path-A force path; for routed/Path-B the marketId travels inside the
quote.
The LP’s slippage guard (numéraire raw, 6-dec). The on-chain create reverts if the
routed premium would exceed it.
SignedQuote
An MM quote envelope. Required when
escapeHatch:'B'; optional for routed (omit → the
EMPTY_QUOTE sentinel → router falls back to the pool).0x${string}
The EIP-712 signature for
quote.'A' | 'B'
Force a specific rail. Omit for routed best-of (recommended). See the create-call
matrix below.
boolean
default:"true"
When not explicitly
false, sends NPM + dUSDC approvals to InflexionCore before the
create call.Hex
The create transaction hash. Throws a clear error if no wallet client is configured.
Which create call fires
createSwapRouted never reverts on a bad MM quote. A malformed or stale quote falls
back to the pool on-chain. Only the executed rail mutates nonce / capacity / lock — see
routing.Approvals — the spender is InflexionCore
WhenbuyProtection runs with approve not explicitly false, the SDK sends two
approvals before the create call:
- NPM
approve(core.inflexionCore, tokenId)— the position NFT to the core. - dUSDC — read
allowance(account, core.inflexionCore); if< maxPremium, sendapprove(core.inflexionCore, maxPremium)— spender is InflexionCore.
getProtectionStatus
Reads the live state of an existing protection (swap): status enum, which rail
underwrote it, and the IL-to-date with the cap applied.
bigint
required
The id returned in the
SwapCreated event from buyProtection.ProtectionStatus | {available:false, reason}
available:false if the swap id is unknown.ilToDate.payout is computed from settlePreview via simulateContract against the
oracle-derived spot, so it mirrors exactly what settle would pay this block.
getClaimableFees
Estimates the Uniswap fees the LP can still collect on the underlying position, from the
NFT’s checkpointed tokensOwed0/1.
bigint
required
ClaimableFees | {available:false}
{ available:false } if the swap is unknown; otherwise the checkpointed owed amounts.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.
settle
The LP (or anyone) settlement write. Recovers the swap’s expiry, finds the bracketing
Chainlink round (round-walk, or your hint), then calls Core.settle to pay the LP
min(realized_IL, MaxIL) from the underwriter’s collateral.
bigint
required
bigint
A Chainlink round id that brackets expiry. If omitted, the SDK walks Chainlink rounds
backwards to find it (
findHintRoundId).Hex
The settlement transaction hash. Throws a clear error if no wallet client is configured.
settle always succeeds within the liveness window (invariant
I8): expiry + LIVENESS_WINDOW + MAX_STALENESS + GRACE_PERIOD. The
round-walk + grace period is the oracle-deadlock fork resolution — a
late or briefly-stale feed cannot strand a settlement.autoProtect
Orchestrates the whole flow — list → preview → buy — across an owner’s eligible positions,
with ACTIVE-protection dedupe. Never throws: every row is a structured success /
skipped / error result.
0x${string}
required
The wallet whose positions to auto-protect.
Per-position premium ceiling (numéraire raw). Rows whose best premium exceeds it are
skipped.number[]
Durations to consider per position.
boolean
If
true, prices and plans but sends no transaction — pure read.AutoProtectResult[]
One row per candidate. Each row is a success (with txHash), a
skipped row (already
ACTIVE, out of range, or over maxPremium), or an error row. The method scans
swaps / nextSwapId for existing ACTIVE protection to avoid double-buying.W (unless dryRun)
Writes one create tx per actionable row. Requires a wallet unless
dryRun:true.poke
Refreshes the σ_ref EWMA for a token before pricing, by calling VolOracle.poke(token).
0x${string}
required
The volatile token whose σ_ref to refresh.
Hex
The poke transaction hash. Throws if no wallet client is configured.
previewPremium({ poke:true })) when σ_ref may be stale and you want the
freshest fair value before signing.
findHintRoundId
A read helper that walks Chainlink rounds backwards from latest to find the round that
brackets a target timestamp — the hintRoundId settle needs.
0x${string}
required
The token whose Chainlink feed to walk.
bigint
required
The target timestamp (the swap’s expiry).
number
Cap on how many rounds to walk backwards.
bigint
The bracketing round id. On an un-fetchable phase boundary it returns the best available
round rather than throwing.
Worked example — preview, buy (routed), settle
{ quote, signature, escapeHatch: 'B' }
where quote is the SignedQuote and signature the EIP-712
sig.
Approvals & decimals at a glance
Framing that must stay exact
- In-range convexity hedge, not “IL insurance”. Entry requires
Pa ≤ P0 ≤ Pb; theinRangegate rejects out-of-range at creation. See the hedge. - Payout =
min(realized_IL, MaxIL); the cap is load-bearing for no bad debt.getPayoffCurve/ilToDateapply it. - The Φ-sum is read on-chain, never reimplemented — only the deterministic load stack is mirrored off-chain (parity-locked), because the deployed lib is delegatecall-only.
Related
LpClient guide
The narrative walkthrough of the LP surface, with the routing flow explained.
Degradation envelopes
Priceable / Degraded / WriteResult — the full envelope taxonomy.Lifecycle
Create → active → settle, end to end.
Routing
How
createSwapRouted picks pool vs MM and never reverts on a bad quote.