sour.finance
Launch app →
LEARN · RISK MODEL

The per-user OI cap on a perp DEX

A per-user open interest cap is the maximum notional position size a single trader can hold on a market before the protocol rejects further opens. On Sour, that cap is not a hard-coded dollar number — it scales with the LP vault that stands behind it, via the formula per_user_cap = lp_nav × risk_budget_bps / max_mm_bps.

Why protocols cap per-user OI

The LP vault on a perp DEX is the counterparty to every open position. When a trader profits, the LP pays. When a trader loses, the LP collects. In aggregate, fees and balanced flow keep the LP healthy. The danger is concentrated, one-sided, leveraged exposure: a single trader who stacks $50M of OI on the same side, into a thin oracle, on the wrong side of a sharp move. The LP can absorb a normal distribution of trader PnL. It cannot absorb arbitrary tail bets.

The aggregate cap — the protocol-wide ceiling on net long-vs-short skew — bounds the LP’s worst-case loss. The per-user cap then bounds how much of that aggregate budget any single account can occupy. It is the slice each individual trader is allowed to carve out of the LP’s collective risk capacity.

Sour’s formula

Sour computes the per-user cap from three inputs, every time the program checks an order:

per_user_cap = lp_nav × risk_budget_bps / max_mm_bps

lp_nav is the current net asset value of the SOUR LP vault, denominated in USDC. It rises when LPs deposit, when fees accrue, or when LPs profit against traders, and it falls when LPs lose against traders or when LPs withdraw. risk_budget_bps is a governance parameter — default 1000 — expressed in basis points. It represents the share of LP NAV the protocol is willing to commit to absorbing trader skew. max_mm_bps is also a governance parameter — default 100 — and represents the maximum share of that risk budget any single market mover (trader account) is permitted to occupy. Lower max_mm_bps means each user gets a larger absolute cap; higher max_mm_bps means more users can saturate the budget in parallel.

With the v1 defaults, per_user_cap = lp_nav × 1000 / 100 = 10 × lp_nav. Each individual trader can hold up to ten times the LP’s total assets in notional open interest. That sounds aggressive in isolation, but remember it is a per-user cap, not an aggregate cap, and it is bounded above by the separate aggregate-OI cap that limits net market skew across all users together.

Walk through an example

Suppose the SOUR LP vault holds $1,000,000 of USDC after deposits and accrued fees. With v1 defaults, the per-user cap is $1,000,000 × 1000 / 100 = $10,000,000 of notional open interest per user.

A single trader could in principle open $10M of notional on SOL-PERP, BTC-PERP, or ETH-PERP. At Sour’s 50× max leverage, that $10M of notional corresponds to roughly $200,000 of posted USDC collateral being levered up. Trying to open beyond the cap — for example, a tenth long that would push the trader over $10M — is rejected at the program level inside the batch handler. The order does not partially fill up to the cap; it is refused and the trader sees an OI-cap-exceeded error.

If LP NAV grows to $5,000,000 — through deposits or accrued fee revenue — the cap automatically rescales to $50,000,000 per user with no governance action. If LP NAV shrinks, the cap shrinks with it. Caps are recomputed on every order check, not snapshotted.

What gets enforced and where

Cap enforcement happens inside the program, in the same handler that runs the batch matching engine. When an order would, if filled at the clearing price, push the submitting trader’s notional OI on that market above per_user_cap, the program returns an error and the order is dropped from the batch. There is no off-chain risk service, no separate keeper, and no privileged role that can override the cap. The check runs deterministically on chain for every order in every slot.

The cap is on notional OI, not collateral. Two traders with the same posted collateral but different leverage settings will hit the cap at different leverage levels. The cap is also per market — a trader at the cap on SOL-PERP can still open positions on BTC-PERP and ETH-PERP up to the cap on each, subject to the aggregate cap and to their available collateral.

Aggregate cap (Option H)

In addition to the per-user cap, Sour enforces an aggregate cap on net OI per market. This is the protocol-wide limit on how lopsided long-vs-short open interest can become before further opens on the heavy side are rejected. The two caps are complementary: the aggregate cap bounds the LP’s worst-case loss across all traders, and the per-user cap bounds any single trader’s share of that bounded budget.

The aggregate-cap design — internally called Option H — is the subject of the Lean general-N model in the formal verification work. Eighteen Kani proofs cover the settlement math and per-batch invariants, and the Lean specification proves the aggregate budget invariant for arbitrary numbers of traders. The verification work lives at /learn/formally-verified-perp-dex and the verification repository is public at github.com/GageBachik/sour-verification.

Trade-offs vs unlimited OI

Capping OI is a deliberate trade against the largest-trader edge case. A market without a per-user cap lets a whale express maximum conviction, which produces deeper apparent liquidity and tighter on-chain spreads. The cost is solvency risk concentrated on the LP backstop: one trader on the wrong side of one move can in principle bankrupt the pool.

For a small protocol still bootstrapping its LP — Sour is at v1.0.7 with a single-digit-millions LP NAV — the right side of that dial is "cap by formula." The cap is permissive enough that no current trader hits it under normal conditions, and it scales automatically with LP NAV. As the LP grows, the absolute per-user cap grows linearly without governance having to vote on a new number every time. As the LP shrinks, the cap protects what is left of LP solvency without requiring an emergency parameter change.

Both risk_budget_bps and max_mm_bps are governance parameters. They will be tuned as the protocol matures. The formula itself — and the property that the per-user cap is a strict slice of the aggregate budget — are model-checked invariants and will not change without a coordinated migration.

At a glance

FORMULA
per_user_cap = lp_nav × risk_budget_bps / max_mm_bps
risk_budget_bps DEFAULT
1000 (10% of LP NAV)
max_mm_bps DEFAULT
100 (1% per market mover)
DEFAULT RATIO
10× LP NAV per user
COMPLEMENT
Aggregate-OI cap (Option H)
ENFORCEMENT
Inside batch handler, every order, every slot
PROGRAM ID
souryQgnM1xiNuGcmVYLPGT3MKqnGN8QTqP8zk8eape
SOURCE
github.com/GageBachik/sour
VERIFICATION
github.com/GageBachik/sour-verification
A per-user OI cap is not a ceiling on ambition. It is a guarantee that no single trader can exit through the LP’s back wall.