# Multi-Asset Collateral — Design & Staged Plan

Status: R&D / design (2026-05-27). Branch: `feat/premium-funding-penalty-upgradeable` (or a new `feat/multi-collateral`).

## Competitive research → model selection (2026-05-27)
Researched the collateral design of Hyperliquid, Drift, Lighter, Pacifica, Extended, Orderly, Binance
(Multi-Asset + Portfolio Margin), dYdX v4, Backpack, Paradex, Aevo (sources in the research agents' reports).

**Headline finding: NO production venue settles perp PnL in non-USD assets.** "Full cross-collateral
settlement" (paying PnL in BTC/ETH) does not exist anywhere — it creates a fatal correlation (position loss
coincides with collateral-value drop) and an oracle/accounting explosion. Everything reduces to a USDC
numeraire. The real spectrum is 3 models that differ only in how non-USDC collateral is drawn down to cover
a USDC-denominated loss:

| Model | Who | Loss → non-USDC collateral | Efficiency | Risk |
|---|---|---|---|---|
| 1. USDC-only | dYdX v4, Paradex(today) | n/a | low | lowest |
| 2. Multi-asset margin, USDC-settled, **negative balance + auto-convert on own book** | Binance Multi-Asset, Aevo, Extended, Orderly, Lighter, Pacifica, HL-PM | sell collateral at oracle×LF when deficit trips threshold | high | correlation + convert-at-worst-time |
| 3. Multi-asset margin, USDC-settled, **auto-borrow against collateral** | Backpack, Pacifica(implicit), HL-PM | borrow USDC vs collateral (interest), convert last | highest | + lending/util/rate risk |

Universal patterns to copy: collateral value = `balance × oraclePrice × weight`; **two-tier weight**
(initial < maintenance; Drift 0.80/0.90, Aevo/Extended/Orderly ETH/BTC ~0.90, USDT ~0.95–0.99); **size-scaling
de-weighting** (Drift IMF `min(0.8, 1.1/(1+0.003√amt))`, Orderly discount factor); **per-asset + per-user
supply caps**; liquidate collateral on the **venue's own spot book** at oracle×liquidation-factor (floor + capped fee).

## DECISION (revised after research)
- **Settlement model: Model 2 — multi-asset MARGIN, USDC-SETTLED.** Non-USDC collateral (USDT/BTC/MON) is
  haircut-valued and backs positions; PnL/funding/fees stay USDC-denominated. A loss creates a negative-USDC
  balance; when it trips a threshold the protocol sells the user's non-USDC collateral **on marginX's own spot
  CLOB** at oracle×liquidation-factor. (Supersedes the earlier "full cross-collateral settlement" pick, which
  no venue does.) **Phase-2 path to Model 3** (auto-borrow via the existing lending/UnifiedMargin system).
- **Accounting: cross-margin BASKET.** Account equity = Σ over enabled collaterals of
  `balance_i × oraclePrice_i × weight_i` (+ unrealized PnL − pending fees), normalized to 18-dec USD.
- **Risk params source: reuse `margin/RiskOracle`** (per-asset weight/riskPrice/maxLeverage, governance-set).
- **Risk controls:** two-tier weights, size-scaling de-weighting, per-asset/per-user caps, liquidate collateral
  first on spot CLOB, oracle-deviation liquidation halt (Drift ≥50%/TWAP), 3-way median collateral price (HL),
  **capped backstop** (HL post-JELLY). **MON (own/gas token) = low weight + tight cap** (HYPE/JELLY reflexivity).

## Custodian facts (confirmed)
- `BalanceManager` = real ERC-20 custodian, already token-agnostic.
- `BalanceManagerVault` (`PERP_PROXY_VAULT`) = live perp margin/accounting layer (locks in BalanceManager).
- `UnifiedVault.sol` = DEAD (never deployed; `unifiedVault` wired to the BalanceManagerVault proxy). Remove
  in a separate follow-up PR; replace the two interface casts (`PositionHandler:696`, `PerpEngine:2535`)
  with a plain `IVault`.

## Current single-collateral assumptions to break
- `CLOBPerpetualRouter:372` `require(collateralToken == usdc)`; PerpEngine equivalent.
- Collateral valued **1:1 USD, no oracle price**, in: `BalanceManagerVault._accountCollateral`,
  `_calculateCrossMarginState`, `_crossMarginEquity18`; `PositionHandler.isPositionLiquidatable`;
  `PerpEngine._isPositionLiquidatable`.
- Single immutable `usdc` + cached `collateralDecimals`/`pnlToCollateralFactor` in BalanceManagerVault,
  CLOBPerpetualRouter, PerpEngine storages.
- `Position.collateralToken` is already per-position (good); position keys already include it.

## Size constraint (HARD)
Modular Arc contracts are near the 24 KB limit (default profile): CLOBRouter 370 B, PositionHandler 335 B,
OrderHandler 945 B headroom. Multi-collateral logic MUST land in libraries / a new external
`CollateralManager` contract, not inline, on the modular arch. Monad `PerpEngine` (72/128 KB) has room.

## Architecture
```
        ┌──────────────────────────────────────────────┐
        │ RiskOracle (existing, margin/)                │  riskPrice, haircut, maxLeverage per asset
        └───────────────┬──────────────────────────────┘
                        │ getRiskParams(token)
        ┌───────────────▼──────────────────────────────┐
        │ CollateralManager (NEW, library or contract)  │  enable/disable, basketValueUsd18(user),
        │   - enabledCollaterals[]                      │  priceUsd18(token), haircutBps(token)
        │   - valuation: Σ bal_i·price_i·haircut_i      │
        └───────────────┬──────────────────────────────┘
                        │
   ┌────────────────────┼─────────────────────────────────┐
   │ BalanceManagerVault │ PerpEngine / PositionHandler     │  consume basket value in equity/health/liq;
   │ (margin accounting)  │ (liquidation gate)              │  multi-asset lock/allocate; settlement engine
   └────────────────────┴─────────────────────────────────┘
                        │ holds tokens
        ┌───────────────▼──────────────────────────────┐
        │ BalanceManager (custody, token-agnostic)      │
        └───────────────────────────────────────────────┘
```

## Staged plan (each stage = full audit workflow + both-arch parity)

**Stage 0 — Collateral registry + RiskOracle wiring.** `ICollateralRegistry`: `addCollateral(token, priceSymbol,
enabled)`, `setEnabled`, governance-gated; pull haircut/riskPrice from RiskOracle. USDC seeded as
`haircut=1, price=1`. No behavior change yet.

**Stage 1 — Basket valuation (READ-ONLY).** `collateralValueUsd18(user)` = Σ enabled `bal_i·oraclePrice_i·
haircut_i`. Swap the 1:1-USD reads in `_accountCollateral`/`_crossMarginEquity18`/`_calculateCrossMarginState`
and `_isPositionLiquidatable` to use basket value. Invariant: with only USDC enabled (haircut=1, price=1),
results are byte-identical to today → safe, regression-guarded.

**Stage 2 — Multi-asset deposit & allocation.** Remove the USDC-only `require`; accept `collateralToken`;
lock/unlock the chosen asset(s) in BalanceManager; track per-asset allocation in the vault.

**Stage 3 — Settlement engine (the hard part).** Realize USD PnL/funding/fees against the basket: debit
policy (USDC first, then other assets at oracle price), profit paid in USDC from the counterparty/vault pool;
record per-asset deltas.

**Stage 4 — Liquidation + insurance for mixed assets.** Seize/convert non-USDC collateral at oracle (or
auction); insurance fund holds/handles mixed assets and bad-debt socialization per asset.

**Stage 5 — Parity + audit + tests.** Mirror Monad↔modular; Pashov+Nemesis; multi-collateral test suite
(non-6-decimal, non-USD, disabled-asset, oracle-stale, liquidation-with-seizure).

## Risks / open questions
- Profit payout asset under full cross-collateral (USDC-only vs proportional) — default: USDC.
- Oracle staleness on a collateral asset must NOT brick liquidation (tie into the existing getPnl/oracle
  try/catch follow-up). A stale collateral price → exclude that asset from equity (conservative).
- Per-asset insurance/bad-debt accounting.
- Modular Arc bytecode budget → externalize to CollateralManager.
