Skip to main content
Inflexion was audited in four distinct passes, each with a different lens: a multi-LLM spec audit (design correctness, pre-build), an adversarial quant audit (the Monte Carlo solvency model), and two static-analysis passes with Slither (the core contracts, then the cvAMM). This page is the honest, reproducible record of every one: what each pass found, what changed, and how to run it yourself.
None of these passes is a substitute for a professional, paid third-party smart-contract audit. They are adversarial, multi-model, and source-cited — and every finding either landed as a code fix or is a named, justified residual. Where a limit remains, it is stated with its why and its mitigation, never naked. See status & caveats for the live-system version of that ledger.

At a glance

Spec audit (pre-build)

Multi-LLM (Claude Opus, GPT-5, Gemini 2.5). 12 fixes applied + 2 architectural forks resolved → spec v3.3. Consensus: FULL/European core sound, no-bad-debt invariant genuine.

Quant audit (Phase 14.11)

Adversarial review of the Monte Carlo solvency model. 6 HIGH findings; 9 fixed in-band, 6 deferred as named mainnet-TODOs. n_runs 1k → 50k; VaR → CVaR.

Slither — core pass

27 findings → 1 fixed (reentrancy-no-eth in settle) + 26 accepted false-positives (idiomatic patterns).

Slither — cvAMM pass (P3.9)

74 results → 0 high, 0 confirmed vulnerabilities, 1 cleanup (a stray template file deleted).

Timeline

The passes run in build order — design correctness first, then the economic model, then static analysis as the code lands.

Spec audit → spec v3.1 (12 fixes)

Pre-build, multi-LLM consensus review of the protocol design. Twelve concrete fixes applied, hardening the spec from its first draft to v3.1. The biggest structural changes: store L at creation (→ I6), reject out-of-range entries, and add on-chain authoritative quote capacity with Permit2 bitmap nonces (→ I7).

Fork-1 resolved → spec v3.2

The oracle settlement-liveness deadlock surfaced by the GPT/Gemini audit — a permanent fund lockup if Chainlink and the pinned TWAP both froze and diverged at expiry. Resolved with round-at-T pinning + lone-spike sanity check + a 24h liveness backstop → invariant I8. See the forks page.

Fork-2 resolved → spec v3.3

The no-last-look firm-quote pickoff flagged GPT High — leaked signed quotes (bearer instruments) picked off when vol gaps. Resolved with a deterministic, oracle-anchored price band that auto-voids the quote on-chain — not last-look → invariant I9. See the forks page.

Quant audit (Phase 14.11)

Adversarial review of the Monte Carlo solvency model with GPT-5 + Code Interpreter and Gemini 2.5 Pro. Six HIGH findings; the model moved to 50k runs and a CVaR (tail-coherent) fund target. Gates the PARTIAL / cvAMM parameter set — no PARTIAL constant is ever hardcoded.

Slither — core pass

Static analysis of the core contracts with Slither 0.11.5. One real medium fixed (the settle reentrancy), the rest triaged as idiomatic false-positives. This was the single Slither finding that produced a code change.

Slither — cvAMM pass (P3.9)

Static analysis of the dual-tranche cvAMM contracts as they landed. Zero high, zero confirmed vulnerabilities; one cleanup (a stray foundry-template file). Manual review confirmed the junior-first-loss waterfall, the run defense, and the structural independence of I1 from the pricing oracles.

1. Spec audit — multi-LLM (pre-build) → spec v3.3

The first pass was a design audit, not a code audit: three frontier models (Claude Opus, GPT-5, Gemini 2.5) reviewed the protocol specification before the contracts existed. The point was to catch design-level mistakes while they were still cheap to fix. Consensus across all three models: the FULL / European-option core is sound, the no-bad-debt invariant is genuine (not a marketing claim), and the deferral of PARTIAL mode is correct. The one meta-risk all three converged on was two-sided market clearing — whether LP willingness-to-pay meets MM quote-widening — which is a strategic, empirical concern that can only be resolved post-deploy with real flow, not a code bug. (This is exactly what the data moat is built to measure.)

The 12 fixes (→ spec v3.1)

Each fix maps to a concrete code or design change; several became numbered invariants.
Fix #12 also confirmed a non-issue. Two auditors flagged the claim that MaxIL = max(IL(Pa), IL(Pb)) — that the in-range maximum sits at a boundary. One re-derived it from scratch and confirmed it: IL(P) is convex on [Pa, Pb] because V_hold is affine and V_lp is strictly concave (d²V_lp/dP² = −¼·L·P^{−3/2} < 0), so its maximum while in range is at an endpoint. The convexity is load-bearing for the whole design — see the MaxIL derivation.

The two forks (→ spec v3.2, then v3.3)

The two issues the audit could not close in-band became the two architectural forks — each had a real fix that required a design decision, and each resolved to “Option B.” Both are now in code and each is a numbered invariant. They get their own full treatment on the forks page; the short version:
The problem (GPT/Gemini): v3.1 pinned settlement to both the Chainlink round-at-T and a pinned Uniswap TWAP. If both froze at T and diverged by more than 2%, settle would revert forever — a permanent fund lockup.The fix: keep the Chainlink round-at-T pin (fairness), but replace the hard TWAP gate with a Chainlink lone-spike sanity check (the round-at-T is “lone” only if it differs from both neighbours by ≥ LONE_SPIKE_BPS = 500 / 5% — real fast moves persist across rounds and pass), plus a LIVENESS_WINDOW = 86,400s (24h) backstop that accepts the round unconditionally after the window. TWAP demoted to a non-blocking advisory event. → I8, implemented in OracleManager.getSettlementPrice.
The problem (GPT High): firm quotes + any clock-based validity feed a one-sided stale-quote pickoff. Signed payloads are bearer instruments — they survive in any hand that copied them, beyond the off-chain engine’s cancel. When vol gaps, the MM is short convexity and a searcher can submit the leaked bytes on-chain.The fix: each SignedQuote carries quotePrice (the oracle price the MM saw at signing) + priceBandBps. At createSwap the contract reads the live oracle — the same read that pins P0, zero extra oracle cost — and auto-voids the quote on-chain, deterministically, if absBps(P_live, quotePrice) > priceBandBps. No MM discretion at fill ⇒ not last-look. → I9, InflexionCore.
Why not just add “last look”? A last-look path — letting the MM reject a fill at the moment of settlement — is the standard FX fix for stale quotes, and it is exactly the abuse pattern auditors flag (the MM fades when the trade goes against it). It also undercuts trustlessness. Why we reject it: firm quotes + a deterministic oracle band achieve the same protection without discretion. The band kills the dominant pickoff, the short validUntil bounds the leakage window, and on-chain selective nonce invalidation lets an MM cancel one quote. The hard rule: there is no last-look path in MM quoting. The residuals the band does not catch — pure vol-only moves with little spot drift, within-band drift the MM chose to bear, brief oracle lag — are acknowledged and small on crypto majors, not blockers. See the forks page.

2. Quant audit (Phase 14.11) — the Monte Carlo solvency model

The quant audit was an adversarial review of the off-chain Monte Carlo model that sizes collateral and gates the PARTIAL / cvAMM parameter set. Reviewers: GPT-5 + Code Interpreter and Gemini 2.5 Pro, run against the model’s actual code and outputs. Result: 6 HIGH findings; 9 fixed in-band, 6 deferred as named mainnet-TODOs. The deferrals are not silent — each is a named TODO with a justification, not a swept-under-the-rug gap.

What changed

A 1,000-run Monte Carlo is meaningless for a 0.1%-tail estimate — at that sample size the tail bucket holds ~1 path, so the estimate is pure noise. Bumped to 50,000 runs so the tail is actually resolved.
The fund_target was sized on Value-at-Risk, which is not coherent and ignores the shape of the tail beyond the quantile. Switched to Conditional VaR (CVaR / expected shortfall) — coherent and tail-aware — so the fund is sized against the average of the worst outcomes, not just the cutoff.
The fee-sufficiency check used median PnL, which hides tail losses. Switched to mean PnL so a fat left tail actually shows up in the criterion.
The collateral-minimum and fund-target are mutually dependent; the original solve could drift. Replaced with a fixed-point bisection that converges the two consistently.
Severe-stress parameters were re-anchored to Terra, FTX, March-2020, and May-2021 — actual historical drawdowns rather than invented numbers.
Added parameter_provenance per field; surfaced the annualised ruin budget (0.1%/30d ≈ 1.21%/year) so the risk is stated in human terms; and added 8 hand-calculated il.py fixtures from the Uniswap v3 whitepaper to pin the IL math against an independent reference.
No PARTIAL constant is ever hardcoded. Why this is a rule and not a preference: hardcoding a PARTIAL parameter (floor curve, fee curve, liquidation buffer, circuit-breaker thresholds, withdrawal delay, exposure caps, first-loss size) was the exact failure the audit flagged — a number baked into the contract can’t track the model that justifies it. Mitigation: every PARTIAL / cvAMM primitive is read from the calibrated risk-parameter set produced by this Monte Carlo. The model is the source of truth for those constants.
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.

3. Solidity static analysis — Slither 0.11.5 (two passes)

Slither was run twice as the code landed: once over the core contracts, once over the dual-tranche cvAMM. The discipline in both passes was the same — triage every finding, fix the real ones, and justify every accepted false-positive rather than silencing the tool.

3.1 Core pass — 27 findings → 1 fixed + 26 accepted

The one real finding was a reentrancy-no-eth medium in settle — and it produced the single most security-relevant code change of the static-analysis work. The fix: status-flip-first, strict CEI. In settle, s.status = Status.SETTLED is hoisted to immediately after the validity checks, before any external call. So a malicious oracle or ilMath re-entering settle on the same swapId hits the Status.ACTIVE precondition and reverts — the re-entrant path is dead.
For the full four-phase Checks-Effects-Interactions ordering on every create path, see CEI / reentrancy. The remaining 26 findings were accepted false-positives, each idiomatic:

3.2 cvAMM pass (P3.9) — 74 results → 0 high, 0 confirmed, 1 cleanup

The cvAMM pass produced zero high findings and zero confirmed vulnerabilities. The one action item was cleanup: a stray foundry-template Counter.sol was deleted (it was the only ^0.8.13 pragma usage in the tree). More valuable than the automated output was the manual review it triggered, which confirmed:
  • the junior-first-loss waterfall in releaseAndDistribute — juniorLoss = min(payout, juniorAssets) first, seniorLoss only after;
  • the withdrawal-cooldown + locked/free run defense — junior cannot be drawn below totalLocked;
  • the lockedAfter ≤ juniorAssets senior-protection check at every lock; and
  • that the FULL no-bad-debt guarantee (I1) is structural and independent of the FairValue / Vol oracles — settle caps payout at MaxIL == collateral regardless of any pricing oracle.
That last point is the headline of the entire security model: no-bad-debt is an algebraic property of a min, not a function of the pricing oracles. The FairValueOracle and VolOracle are load-bearing for the I10 price cap and depositor solvency — not for I1. See the security overview.

4. Reproduce it yourself

Every pass is reproducible from the repo. The exact commands:
The invariants that the spec and quant audits hardened are not just documented — they are machine-verified every CI run: See the invariants page for each invariant’s precise statement, why it holds, where it is enforced (file:line), and the test that verifies it.
CI scope — stated plainly, with its why and mitigation. Limit: GitHub Actions runs only forge fmt + build + test. cargo stylus, Node, and Python validation are run manually (WSL2 / Nitro). Why: the Stylus toolchain and a local Nitro fork don’t run cleanly in the hosted CI runner. Mitigation: the Solidity FairValueOracle is exercised in CI — it’s what forge test compiles — and it serves as the revm-executable cross-check of the shipped Stylus oracle. The Φ-sum is never reimplemented off-chain; the Solidity copy exists precisely to be CI-testable against the production Stylus pricer.

What the audits do — and don’t — establish

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.
The audits establish that the design is internally consistent, the no-bad-debt invariant is genuine and oracle-independent, the solvency model is sized with a coherent tail measure, and the contracts carry no high-severity static-analysis findings. They do not replace a paid professional audit, and they do not — and cannot — resolve the meta-risk all three spec auditors named: two-sided market clearing, which is empirical and only answerable with live flow.
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 ten invariants

Precise statement, why it holds, file:line enforcement, and the Foundry fuzz test for each of I1–I10.

The two resolved forks

Fork-1 (I8, settlement liveness) and Fork-2 (I9, oracle-anchored band — not last-look) in full.

Attack-vector ledger

Every audited vector mapped to its mitigation and the invariant / file:line that closes it.

Status & caveats

The live-system version of this ledger — every pending state with its why and path forward.