Skip to main content
Two things will silently break an integration if you get them wrong: a hardcoded address that drifts from the live registry, and a decimal mistake that turns $8.93 into $8,930,000 (or back). This page nails both. Every address comes from the on-chain address registry via @inflexion/sdk, and every premium, MaxIL, and payout you read is a 6-decimal raw bigint — not a float.
The SDK hardcodes nothing. It loads the registry and re-exports typed groups; createInflexionSdk never takes an address argument. Import from the package and you inherit the live deployment automatically. See also Addresses & deployments and Decimals & units.

Import addresses — never hardcode

The SDK exports the registry as typed groups, plus the chain id constant. Import what you need; do not paste a 0x… literal into application code (mirrored in the guardrails).
import the registry groups
CHAIN_ID === 421614 is the default EIP-712 chain id for quote signing.
Limit: these addresses are Arbitrum Sepolia testnet values, not Arbitrum One. Why: the protocol is live on Sepolia (chainId 421614) — a fresh full redeploy on 2026-06-05. Mitigation: never copy a Sepolia address into a mainnet config. When the protocol ships to Arbitrum One, the registry file changes and your imports follow it for free — that is exactly why you import the symbol, not the literal. The mainnet reference feeds are migration references only, never the live deployment.

The symbol → address → role map

These are the addresses the SDK resolves. The canonical, always-current table lives in the deployments reference; it is reproduced here so you can match an exported symbol to its on-chain role at a glance. 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.

Which SDK symbol maps to which row

Limit: the deployed Solidity libraries — libs.cvammPricing, libs.tickMath, libs.swapMath, libs.quoteVerification — revert on a direct eth_call. Why: Solidity guards deployed library functions against being called outside a delegatecall context; the library image runs only inside InflexionCore’s delegatecall during pricing (confirmed on the 2026-06-05 deploy). Mitigation: the SDK never reads CvammPricing directly. It keeps a parity-locked TypeScript mirror of the load math — the cvamm namespace — that is byte-equal to the deployed Solidity. The Φ-sum, by contrast, is read on-chain from the Stylus FairValueOracle and never duplicated.

Decimals & units — the load-bearing conventions

Get this table into your head before you write a single read or amount. The single rule: a field named *Wad is 1e18-scaled; a USDC amount is a 6-decimal raw bigint; a price is feed-decimal raw unless the field name says Wad.
The single most common integration bug: treating a 6-decimal premium as dollars. preview.best === 8_930_000n means $8.93, not $8,930,000. Why: dUSDC is the 6-decimal numéraire and the SDK returns raw on-chain integers — it never silently converts to a float, because float rounding would corrupt the on-chain maxPremium slippage guard. Mitigation: format only at the display edge — Number(premium) / 1e6 for a label, and keep the raw bigint for any value you send back on-chain (e.g. maxPremium).

Reading a premium safely

6-dec dUSDC: raw bigint in, label out

WAD ratios and basis points

Loads and rates come back in WAD. To present a load as a percentage, divide by 1e18; to convert WAD → basis points the SDK exports wadToBps: bps=⌊wad⋅100001018⌋1 bps=1014 wad (BPS_TO_WAD)\text{bps} = \left\lfloor \frac{\text{wad} \cdot 10000}{10^{18}} \right\rfloor \qquad\qquad 1\ \text{bps} = 10^{14}\ \text{wad}\ (\texttt{BPS\_TO\_WAD})
WAD → %, WAD → bps
Internally the on-chain Φ-sum lifts WAD to 1e24 for transcendental precision and rounds back to WAD half-up at the ABI boundary. You never see 1e24 — every value the SDK hands you is already at the WAD (1e18) or 6-dec scale.

sqrtPriceX96 conversions

Uniswap v3 stores prices as a Q64.96 fixed-point square root, \sqrt{P}\cdot 2^{96}. The SDK gives you both directions you need: sqrt → human price and sqrt bounds → the a/b ratios the pricer consumes.

sqrtPriceX96 → price (WAD)

sqrtToPriceWad(sqrtPX96) returns the price of token0 in token1, WAD-scaled: Pwad  =  (sqrtPX96296)2⋅1018P_{\text{wad}} \;=\; \left(\frac{\text{sqrtP}_{X96}}{2^{96}}\right)^{2}\cdot 10^{18}

sqrtPriceX96 bounds → a, b (the pricer inputs)

The fair-value oracle and the load stack are L-independent: they depend only on the geometry ratios a=Pa/P0a = P_a/P_0 and b=Pb/P0b = P_b/P_0, σ_ref, and duration. cvamm.abFromSqrt(sqrtP0, sqrtPa, sqrtPb) derives them from the position’s three sqrt prices: a  =  (sqrtPasqrtP0)2 ⁣,b  =  (sqrtPbsqrtP0)2a \;=\; \left(\frac{\text{sqrtP}_a}{\text{sqrtP}_0}\right)^{2}\!, \qquad b \;=\; \left(\frac{\text{sqrtP}_b}{\text{sqrtP}_0}\right)^{2} Both returned in WAD as { aWad, bWad }. Note the cancellation of the 2962^{96} factor — abFromSqrt is a pure ratio, so it is unit-clean regardless of the Q64.96 scale.
sqrt → price, sqrt bounds → a/b
You rarely call abFromSqrt by hand. lp.resolveGeometry (and mm.getPositionGeometry) already compute aWad/bWad and the inRange flag for you from NPM.positions(tokenId) + TickMath. The standalone helper is there for when you have raw sqrt prices and want the ratios without a position read — see the cvAMM port.

The in-range gate, in price terms

The same condition the SDK derives is the on-chain createSwap entry gate — this is what makes it an in-range convexity hedge, not “IL insurance”: Pa  ≤  P0  ≤  Pb⟺sqrtPa  ≤  sqrtP0  ≤  sqrtPbP_a \;\le\; P_0 \;\le\; P_b \quad\Longleftrightarrow\quad \text{sqrtP}_a \;\le\; \text{sqrtP}_0 \;\le\; \text{sqrtP}_b An out-of-range entry is rejected at creation. MaxIL — the cap and the unit of risk — is then pure geometry over aa, bb, LL, P0P_0, frozen at creation.

marketId — derived, not read

A marketId is not an address you import; it is deterministically derived from the pair, fee, and duration, identical on-chain and in the SDK. The standalone helper is computeMarketId: marketId  =  keccak256(abi.encodePacked(token0, token1, fee, durationSeconds))\text{marketId} \;=\; \texttt{keccak256}\big(\texttt{abi.encodePacked}(\text{token0},\ \text{token1},\ \text{fee},\ \text{durationSeconds})\big) with token0 = dWETH, token1 = dUSDC (token0 < token1 by address), types (address, address, uint24, uint32).
derive a marketId (matches InflexionCore.registerMarket exactly)
Because duration is baked into the id, the same geometry yields a different marketId — and a different price — per duration (the cvAMM publishes three prices for one MaxIL). The 9-market grid is 3 fee tiers × 3 durations. For the full parity story between on-chain registerMarket and the SDK, see marketId parity.
A SwapRecord carries no marketId on-chain (it is compact). resolveMarket(client, swap) recovers it by reproducing the same keccak256(...) from NPM.positions(tokenId). You only need this when scanning historical swaps; live pricing flows already have the marketId in hand.

Quick reference

Full deployments registry

Every live address with its role — the canonical table this page draws from.

Decimals & units reference

The numéraire conventions, WAD/bps constants, and Q64.96 internals in one reference page.

The cvAMM math port

abFromSqrt, the load stack, and why the off-chain mirror is parity-locked to the delegatecall-only library.

Getting started

Wire createInflexionSdk for the browser (wagmi) or a server (key-based) and read your first premium.