Skip to main content
createSwapRouted is the entry point an LP should almost always use. In a single transaction it prices both underwriting rails off the same on-chain fair value, gives the LP the cheaper of the two, and — this is the load-bearing property — falls back to the always-on pool whenever the MM quote is not strictly better or not usable, without ever reverting on the MM’s account. It is the concrete expression of the floor/ceiling framing: the cvAMM pool (Path A) is the floor of liquidity — always a code-capped price — and the MM rail (Path B) is the ceiling of price — a pure price-improvement layer that can only ever undercut the floor, never degrade it. The router is what wires those two facts together for the LP.

One FairPremium

Both rails priced off the same FairPremium, single VolOracle poke.

Strict-beat, ties to A

The MM wins only if premiumB < premiumA. A tie resolves to the pool.

Never reverts on the MM

A bad / stale / absent MM quote silently falls back to the pool — zero trace (I7).

The one-line decision

The entire routing rule is one boolean:
Everything else on this page is the machinery that makes those three sub-expressions — usableB, premiumB, and premiumA — honest, comparable, and side-effect-free until exactly one rail is executed.
The router emits SwapRouted(swapId, pathB, premiumA, premiumB) on every fill. The chosen rail, and both candidate prices it was choosing between, are written to the chain — the cheapest-wins decision is fully auditable after the fact, and feeds the pool-vs-MM spread signal of the data moat.

Step 1 — both rails priced off the same FairPremium

A fair comparison requires a common baseline. The router computes one FairPremium for the position and prices both rails on top of it, so the only thing that differs between premiumA and premiumB is the load, never the fair value underneath. FairPremium=fairRate⋅MaxIL,fairRate=EQ ⁣[min⁡(IL, MaxIL)]MaxIL\text{FairPremium} = \text{fairRate}\cdot\text{MaxIL}, \qquad \text{fairRate} = \frac{\mathbb{E}_{\mathbb{Q}}\!\left[\min(IL,\,\text{MaxIL})\right]}{\text{MaxIL}} The shared read prologue (_prepareSwap) and the fair-value read (_fairPremium) run once; the VolOracle is poked exactly once to refresh σ_ref, and both rails read the result. The two candidate premiums are then the same FairPremium with two different loads layered on: premiumA=⌈ FairPremium⋅(1+min⁡(baseLoad+util_skew+disp_skew⏟totalLoad, maxLoad))⌉premiumB=⌈ FairPremium⋅(1+loadBps/104)⌉,loadBps≤maxLoadBps\begin{aligned} \text{premium}_A &= \big\lceil\,\text{FairPremium}\cdot\big(1 + \min(\underbrace{\text{baseLoad} + \text{util\_skew} + \text{disp\_skew}}_{\text{totalLoad}},\ \text{maxLoad})\big)\big\rceil \\[4pt] \text{premium}_B &= \big\lceil\,\text{FairPremium}\cdot\big(1 + \text{loadBps}/10^4\big)\big\rceil,\qquad \text{loadBps} \le \text{maxLoadBps} \end{aligned} Both are additionally capped at MaxIL (never charge more than the maximum possible payout), and the Path-A load sum is hard-clamped by invariant I10, min(..., maxLoad), by construction in code. Path A is computed by _pricePathAFromFair; Path B’s price comes back from the usability mirror described next. See the pricing stack and the load stack for how each load is built.
Pricing both rails off the same FairPremium is also what makes the clearing-load and pool-vs-MM spread signals non-circular: the fair value is a fixed, published reference, so the load the market clears at is an observable behavioral quantity, not a back-solved artifact.

Step 2 — usableB: the non-reverting mirror of Path B

This is the heart of the routing safety story. The router does not call the reverting Path-B gate. It calls _quoteUsableAndPremiumB, a view function that mirrors every Path-B check and returns (usableB, premiumB) — (false, 0) instead of reverting whenever any check fails. It returns (false, 0) if the MM quote is any of:
No quote supplied, model != FULL, or the position/premium fails the MIN_POSITION_V0 = $100 / MIN_PREMIUM = $1 dust floors.
loadBps > maxLoadBps — the same I10 ceiling the reverting path enforces, evaluated as a boolean here instead of a revert.
validUntil <= now, or secondsAhead outside the [5s, 15s] window.
priceBandBps outside [25, 500], or the live oracle has drifted beyond the band: absBps(P_live, quotePrice) > priceBandBps — see price pinning.
The LP’s ratioBps = maxIL·1e4/V0 outside the quote’s MaxIL-ratio band; consumedNotional[quoteId] + V0 > maxNotionalV0; or the bitmap nonce already used.
availableBalance(mm) < maxIL; a zero quotePrice; or the EIP-712 / EIP-1271 signature failing verification (checked via STATICCALL).
The critical property: _quoteUsableAndPremiumB is view. It reads isNonceUsed, consumedNotional, availableBalance, and the signature (via STATICCALL), but it never calls _useNonce, never increments consumedNotional, and never locks collateral. A quote that is evaluated but not chosen mutates no state whatsoever — it leaves zero trace.
A bad / stale / absent MM quote falls back to the pool — it never reverts the LP’s transaction.
  • Limit: an MM quote passed to createSwapRouted can be absent, expired, stale, out-of-band, over-load, over-capacity, undercollateralized, zero-priced, wrong-model, or simply not cheaper than the pool.
  • Why it cannot harm the LP: the router evaluates the MM quote through a read-only mirror (_quoteUsableAndPremiumB, view), so any failing condition merely sets usableB = false — it cannot revert and cannot mutate state.
  • Mitigation (by construction): the swap silently routes to the always-on cvAMM pool. Only protocol-level failures revert (see below). An LP using createSwapRouted is therefore never worse off than calling createSwapPathA directly.

Step 3 — the route: strict-beat, ties to Path A

Two design choices are deliberate and load-bearing:
1

Strict-beat (< not ≤)

The MM wins only if it strictly undercuts the pool. This is what makes the MM a genuine price-improvement layer: it captures flow only when it offers the LP a real saving, not when it merely matches.
2

Ties resolve to Path A

When premiumB == premiumA, useB is false and the swap goes to the pool — the dependency-free, always-on rail. An MM cannot divert flow by matching the pool’s price; it must beat it. This protects the floor: the rail with no relayer, no validity clock, and no off-chain dependency is the default.
The result is the LP always pays min(premiumA, premiumB_if_usable) and is always served — the pool floor guarantees a fillable price exists, and the MM ceiling can only push the LP’s cost down.

Step 4 — only the executed rail mutates state (I7 preserved)

After the boolean resolves, exactly one rail executes, and only that rail mutates nonce, capacity, or lock:
This is the router-level statement of invariant I7 — capacity authority:
consumedNotional[quoteId] ≤ maxNotionalV0, and a cancelled bitmap-nonce bit can never fill.
Because the losing rail’s usability is evaluated through a view mirror, a Path-B quote that loses the route consumes no capacity, burns no nonce, and locks no collateral. An MM’s quote cannot be silently drained by an LP who ends up routing to the pool — the on-chain ledger only ever moves for the rail that actually filled.
On Path A, the lock additionally passes the pool’s structural checks — free ≥ amount and the senior-protection invariant totalLocked ≤ juniorAssets. So even the fallback rail enforces depositor-protection at lock time; the router never bypasses it.

What does revert — and why those are correct

The router does not revert on the MM. It does revert on protocol-level failures, because those are not “the MM quote was bad” — they are “this swap cannot exist at all”: In every one of these cases, the swap is invalid for both rails — so reverting is the correct, honest outcome. The router’s promise is narrow and precise: a bad MM quote never reverts; an invalid swap always does.

The LP-always-served guarantee, stated precisely

Putting the pieces together, createSwapRouted gives the LP a guarantee that no single-counterparty design can:
1

A fillable price always exists

The pool is the always-on floor. Whenever the swap is valid at all (owner, in-range, market wired, above dust), the pool can fill it at a code-capped (I10) price.
2

The MM can only improve it

The MM rail is a read-only price-improvement layer. It wins only by strictly beating the floor; it can never make the LP pay more, and a tie keeps the LP on the pool.
3

A misbehaving MM is invisible to the LP

Stale, leaked, undercollateralized, or absent MM quotes degrade to the pool with no revert and no state change. The LP’s experience is identical to the MM not existing.
4

The settlement core is the same either way

Whichever rail filled, settle is the single untouched core: pin the Chainlink round-at-T, compute IL with the stored L (I6), pay min(realized_IL, MaxIL) (I1 + I2) from the rail’s own vault, return the NFT.
This is why the no-bad-debt guarantee is independent of how the quote was discovered — routing decides who underwrites and at what price, never whether the LP is paid. That is enforced downstream at settlement against the chosen counterparty’s own collateral.

Worked example — live on Arbitrum Sepolia

The 2026-06-05 redeploy ran a real routed create→settle against one 300s market:
createSwapRouted priced both rails off one FairPremium:
  • premiumA_cvamm = $13.80 (pool, with its on-chain load stack)
  • premiumB_mm = $8.93 (MM, loadBps 1000)
Since usableB == true and 8.93 < 13.80, useB was true → the MM won. MaxIL $3,215.65 was locked from the MM’s own UnderwriterVault collateral, realized IL came to $245.66, payout $245.66, settled from the MM’s collateral. The pool’s nonce/capacity were untouched — only the executed rail moved.
The SwapRouted event on swap #2 carries (premiumA = 13.80, premiumB = 8.93, pathB = true) — the entire routing decision, both candidate prices included, is on-chain and auditable.

The two separate claims

Routing changes who underwrites and what the LP pays — it does not change which guarantees apply. The LP-facing guarantee is enforced identically at settlement for both rails: And the capital-at-risk disclosure applies to whoever the router put on the other side — the pool’s depositors on Path A, the market maker on Path B:

Where to go next

The two rails

The floor/ceiling framing and the one-settlement-core architecture in full.

Path A — the cvAMM

The always-on pool: pricing, dual-tranche collateral, run defense.

Path B — the MM rail

Firm EIP-712 quotes, no last-look, and the three on-chain protections.

The pricing stack

How FairPremium, the load stack, and the I10 cap compose into a premium.

Capacity authority (I7)

Why only the executed rail mutates nonce/capacity/lock.

The full lifecycle

Create → active → settle, identical regardless of which rail filled.