MM0 Protocol Specification
Version: 0.1 (Stage 0) · Status: draft for review · Applies to: V1 (one token, one vault, one venue, one strategy)
EVERY TOKEN GETS A MARKET MAKER. MM0 makes markets. It does not fake markets.
1. Scope and thesis
MM0 lets an onchain asset fund and attach its own autonomous market maker. A project deposits real, disclosed market-making capital into a Maker Vault. MM0 deploys and manages that capital across approved onchain liquidity venues inside a strict, public Maker Mandate.
The quality of MM0 is measured by how much legitimate market quality it creates per dollar of risk capital, never by how much volume it can generate.
1.1 Non-goals (V1)
Cross-chain MM, CEX accounts or API-key custody, leverage, perps, borrowed inventory, options hedging, derivatives, HFT order-book quoting, MEV extraction, sniper/holder/volume bots, price support, and any form of wash or self-trading. V1 is one token, one vault, one venue, one strategy.
1.2 First principle: no price objective
MM0 never targets price, market cap, volume, transaction count, holder count, or chart appearance. MakerMandate.priceTarget is typed as the literal null, and validateMandate rejects any other value.
2. The Maker
THE MAKER = TOKEN + MAKER VAULT + MAKER MANDATE + MARKET OBSERVER
+ STRATEGY ENGINE + RISK ENGINE + EXECUTOR + PUBLIC LEDGER
Schema: MakerInstance in src/domain/types.ts. Every field in the brief is represented: id, chain, governedToken, quoteAssets, makerVault, mandate (+hash), approvedVenues, strategyConfiguration, riskConfiguration, currentInventory, activePositions, performance, executor, eventLedger, emergencyControls.
2.1 Intelligence stack (seven layers)
| # | Layer | Module | Nature | Latency |
|---|---|---|---|---|
| 1 | Market Observer | src/market-data/observer.ts |
numeric snapshots only | every tick |
| 2 | Quant Strategy Engine | src/strategy/engine.ts |
deterministic | every tick |
| 3 | Inventory Controller | src/inventory/controller.ts |
deterministic | every tick |
| 4 | Range / Liquidity Planner | src/strategy/range-engine.ts |
deterministic, closed-form | every tick |
| 5 | AI Supervisor | src/supervisor/supervisor.ts |
slow, advisory, schema-bound | hours |
| 6 | Risk Engine | src/risk/engine.ts |
pure function, hard constraints | every intent |
| 7 | Executor | src/execution/executor.ts |
build → verify → re-simulate → send | every approved intent |
The LLM is never the trading engine. The supervisor can only propose one of: strategy selection, widthMultiplier, reserveAdjust. Each is bounded by mandate.strategyBounds, published to the ledger, and activated after a timelock. Risk-increasing changes must also pass a replay simulation gate (§9).
2.2 Control flow (per tick)
MARKET DATA → OBSERVER → REGIME → ACCOUNTING → BREAKERS → STATE
→ (SUPERVISOR, slow) → STRATEGY PLAN → RISK ENGINE ─ fail → REJECT (ledger)
└ pass → EXECUTOR
EXECUTOR: build → verify tx → re-simulate → send → RECEIPT (ledger)
Implemented in Maker.tick() (src/maker/maker.ts). There is no code path from strategy, supervisor or admin to the venue that bypasses evaluateIntent and verifyTransaction.
3. Objects
| Object | Purpose | Type |
|---|---|---|
MakerMandate |
immutable constitution: assets, venue, limits, permissions, safe modes | types.ts |
MakerVault |
vault address, PDA authority, base/quote/gas accounts | types.ts |
StrategyConfig |
strategy parameters, which may vary only inside the mandate box | types.ts |
MarketSnapshot |
numeric market state: spot, TWAP, reference, vol, volume, depth | types.ts |
VolatilityState |
regime + numeric reasons | types.ts |
InventoryState |
base%/quote%, skew, band status | types.ts |
RangeProposal |
target range + estimates (exit prob., utilization, fee yield) | types.ts |
ExecutionIntent |
ordered legs + reason codes + structured rationale | types.ts |
RiskDecision |
per-check pass/fail with detail | types.ts |
ExecutionReceipt / MakerAction |
permanent public record of every action | types.ts |
PerformanceSnapshot |
NAV, P&L split, depth, inventory, state | types.ts |
CircuitBreakerEvent |
breaker trigger/clear with severity | types.ts |
3.1 Intent legs
Only four leg kinds are executable: ADD_LIQUIDITY, REMOVE_LIQUIDITY, COLLECT_FEES, SWAP (approved pair only). FORBIDDEN_LEG_KINDS (TRANSFER_EXTERNAL, BORROW, LEND, LEVERAGE, BRIDGE, APPROVE_PROGRAM, STAKE, SET_AUTHORITY, BUY_ASSET) exist in the type system only so tests can prove they're rejected. No builder emits them, and no venue adapter can translate them.
4. Maker Mandate
The mandate defines the box. Presets live in src/domain/mandate.ts:
| Limit | CONSERVATIVE | BALANCED | ACTIVE |
|---|---|---|---|
| max venue exposure | 60% | 80% | 90% |
| min reserve | 35% | 20% | 10% |
| base inventory hard band | 30–70% | 25–75% | 20–80% |
| soft band | ±10pp | ±10pp | ±12pp |
| max daily turnover (swaps + net new deployment) | 40% | 75% | 100% |
| max action size | 5% | 10% | 15% |
| max price impact | 0.40% | 0.40% | 0.60% |
| max oracle deviation | 2% | 2% | 2% |
| max daily loss (market-making, vs HODL) | 2% | 3% | 5% |
| max drawdown (market-making, vs HODL) | 7% | 10% | 15% |
| max absolute drawdown (defensive backstop) | 35% | 50% | 60% |
| rebalance cooldown | 30 min | 15 min | 10 min |
| max re-ranges / day | 4 | 8 | 16 |
| AI unavailable | no new risk | quant continues | quant continues |
ACTIVE is not marketed as more profitable. It deploys more capital in narrower ranges, re-ranges more often, and accepts larger loss limits. That means more fee capture and more divergence loss, adverse selection and drawdown.
Turnover definition. Turnover is measured as swap notional plus net increase in deployed value. Re-ranging the same capital (remove → add) isn't turnover, but it's capped separately by maxRerangesPerDay and the cooldown. Initial deployment therefore ramps, one maxActionSize step per cooldown.
Mandate validation (validateMandate) rejects: any forbidden permission set to anything but false; a non-null price target; inconsistent inventory bands; maxDeploy that violates the reserve or venue exposure; undisclosed funding sources. The mandate hash (sha256(canonicalJson(mandate))) is published to the ledger at creation and stamped on every receipt.
5. Objective function
MM0 does not optimize gross volume. For an evaluation window [0, T] with no external flows (flows are removed by unitization, see the Accounting Spec):
J(θ) = F_T legitimate LP fees earned (collected + accrued)
+ w_D · ∫₀ᵀ D_t dt value the mandate assigns to MM0's ±2% depth contribution
− L_T divergence loss (LP value vs HODL, excl. fees, costs, own swaps)
− R_T rebalance cost: impact + fees paid on MM0's own inventory swaps
− C_T transaction costs (base + priority fees, rent not refunded)
− λ_I · ∫₀ᵀ (s_t − s*)² · NAV_t dt inventory risk: squared deviation of base share from target
− λ_DD · MDD_T · NAV_0 drawdown penalty
− λ_C · ∫₀ᵀ HHI_t · NAV_t dt venue concentration (zero in single-venue V1)
subject to every mandate constraint holding at every action (§6), where:
θis the strategy's parameter vector (width multipliersk_r, deploy fractionsd_r, thresholds), which may vary only insidemandate.strategyBounds;s_tis base share of NAV,s*is the mandate target;D_tis MM0-owned quote value executable within ±2% of the active price;w_D,λ_I,λ_DD,λ_Care mandate-specific (objectiveWeights).w_D = 0by default, which makesJpurely economic. A project that values depth as a public good can setw_D > 0explicitly and publicly.
There is no universally optimal weighting. The strategy is a parametric policy π_θ, and θ is calibrated to maximize E[J] on in-sample data, then validated out-of-sample (walk-forward, Stage 2). MM0 never claims the policy is optimal, only that its parameters were chosen and tested this way.
In code: the simulator reports every component (RunMetrics: fees, divergence, costs, drawdown, depth, time in range). The replay gate uses J = netPnL − λ_DD · MDD · NAV₀.
6. Invariants
Every executable action satisfies all of the following, checked by the risk engine (evaluateIntent) before execution and by verifyTransaction before signing. IDs match RiskCheckId.
| ID | Invariant |
|---|---|
I1 SCHEMA_VALID |
Intent is structurally valid. Unknown kinds, non-finite or negative amounts are rejected. Untrusted input fails closed. |
I2 ACTION_KIND_ALLOWED |
Only add / remove / collect / approved-pair swap. No transfer, borrow, lend, leverage, bridge, approve, stake, authority change, or foreign asset. |
I3 SINGLE_VENUE, VENUE_APPROVED |
All legs target one approved venue. Pool address and program IDs match the mandate. |
I4 ASSETS_APPROVED |
Pool mints equal the mandate base and quote mints. |
I5 SIMULATION_OK |
The intent simulates successfully against current state. |
I6 VAULT_RECONCILED |
Vault balances equal ledger-derived expected balances. Otherwise HALT. |
I7 STATE_PERMITS |
Risk-increasing only in MAKING / DEFENSIVE. Nothing at all when halted. |
I8 NO_NEW_RISK_FLAGS |
No risk-increasing action while any NO_NEW_RISK breaker, supervisor outage (if the mandate requires it), quant outage, or loss limit is active. |
I9 ORACLE_FRESH |
Risk-increasing actions require a fresh independent oracle. Exception: bootstrap with no oracle configured, if the mandate allows it, and never with swaps. |
I10 ORACLE_DEVIATION |
Risk-increasing actions require venue/oracle deviation ≤ limit. A breach also trips the circuit breaker. |
I11 MAX_ACTION_SIZE |
Net deployment increase and each swap ≤ maxActionSize · NAV. |
I12 DAILY_TURNOVER |
Rolling 24h (swaps + net new deployment) ≤ maxDailyTurnover · NAV. |
I13 MIN_RESERVE |
After an add: undeployed value ≥ minReserve · NAV, or not reduced. |
I14 VENUE_EXPOSURE |
After an add: deployed ≤ maxVenueExposure · NAV, or not increased. |
I15 INVENTORY_HARD_LIMIT |
Post-action base share is no further outside the hard band. While outside it, no liquidity or swaps on the accumulating side. |
I16 PRICE_IMPACT |
Each swap's impact ≤ maxPriceImpact. |
I17 SWAP_PRICE_VS_REFERENCE |
Swap average price within maxOracleDeviation of the reference, adverse side. |
I18 SELF_TRADE |
No swap while MM0 has liquidity in that venue. A swap must follow a full removal, and no add may precede it in the same transaction. |
I19 COOLDOWN, RERANGE_LIMIT |
Risk-increasing actions respect the cooldown. Re-ranges ≤ daily cap. |
I20 RANGE_SANITY |
Half-width inside the mandate bounds. The range sits within maxRangeDistance of the reference price. |
I21 DAILY_LOSS, DRAWDOWN |
No risk-increasing action beyond the market-making loss limits (unit NAV vs unitized HODL benchmark; see Risk Model §5). |
I22 TX_COST |
No risk-increasing action when the estimated tx cost exceeds the mandate cap. |
I23 EXECUTION_HEALTH |
No risk-increasing action after too many failures in the last hour. |
| I24 (verify) | Every instruction targets an approved program, implements an authorized leg, uses approved mints, is signed by the vault authority, and moves funds only vault→venue (add / swap-in) or venue→vault (remove / claim / swap-out). Amounts ≤ authorized. Slippage bounds no looser. |
| I25 (verify) | The re-simulated outcome matches the risk-approved simulation (vault deltas, positions, swap count) within tolerance. Otherwise reject. |
| I26 (accounting) | NAV − contributed = realized + feesCollected + uncollected − txCosts + unrealized at every tick (residual ≤ 1e-6·NAV). A breach trips VAULT_INVARIANT → HALT. |
| I27 (ledger) | The ledger is append-only and hash-chained. Any edit or deletion is detectable. |
Invariants I1–I23 are unit-tested in test/risk-invariants.test.ts, I24–I25 in test/execution.test.ts, and I26–I27 in test/core.test.ts. I8 is also checked externally: ledgerTimelineChecks replays the public ledger and confirms that no risk-increasing action was executed under a blocking condition, which is what an outside auditor would do.
7. Agent states and modes
INITIALIZING → OBSERVING (warm-up) → MAKING ⇄ DEFENSIVE
↓ ↑
REBALANCING (transient, inside an action)
any → CIRCUIT_BREAK (CIRCUIT_BREAK/HALT breaker) · PAUSED (emergency authority)
· OUT_OF_CAPITAL · SHUTTING_DOWN
Modes: BOOTSTRAP (history < minHistoryMinutes or no oracle), NORMAL, DEFENSIVE, SAFE. The bootstrap caps (lower deployment, wider ranges, no swaps without an oracle) apply alongside defensive caps.
Safe mode on CIRCUIT_BREAK or PAUSED follows the mandate: WITHDRAW_TO_VAULT (default) removes all liquidity and performs no swaps. Withdrawal is a non-trading operation and does not panic-sell. HALT (vault invariant failure) freezes everything, withdrawals included, until a human reset, because the maker can no longer trust its own view of the vault.
8. Range engine and strategy (V1: VOLATILITY_ADAPTIVE_CL)
Inputs: reference price, spot, σ (EWMA, half-lives 1h/24h, σ_eff = max(short, long), prior-seeded for new tokens), regime, inventory, NAV, 24h volume, fee rate, external liquidity near the active price.
h = clamp(k_r · m_w · m_boot · σ_eff · √T, h_min, h_max) log half-width
shift = clamp(g · (s − s*), −0.5, 0.5)
center = ref · exp(shift · h) range = [center·e^(−h), center·e^(h)]
deploy = min(d_r, maxDeploy, 1 − minReserve − reserveAdjust, maxVenueExposure, caps(mode))
P(exit) = 1 − Σ_{n odd} 4/(nπ) · sin(nπx/L) · exp(−n²π²σ²T / 2L²) x = ln(spot/lower), L = 2h
U = E[min(τ, T)] / T time-to-first-exit utilization
E[fees] ≈ V₂₄ · (T/24h) · fee · share · U, share = m/(m + ext) per price step
P(exit) is the exact survival probability of driftless Brownian motion in an interval (validated against Monte Carlo in test/core.test.ts). All outputs are estimates under a lognormal model, and receipts present them as such.
Decisions. The engine first collects fees above threshold. It then reduces deployment when it is above target (risk-reducing, always allowed). With no positions it deploys. With positions, it re-ranges on OUT_OF_RANGE (after a grace period), EDGE_PROXIMITY, EXIT_PROBABILITY, REGIME_WIDTH_CHANGE, INVENTORY_HARD_LIMIT, or INVENTORY_SKEW. Otherwise it tops up toward target, and doing nothing is a valid and frequent outcome. A re-range is atomic: REMOVE ALL → optional bounded SWAP → ADD, so an inventory swap never trades against MM0's own liquidity.
Inventory. Soft breach: the range is skewed so the over-held asset sits on the side that sells it. Hard warning: in addition, an optional bounded swap toward the soft edge is made, capped by action size, turnover and impact, and only with a fresh oracle. Hard breach: liquidity goes only on the reducing side (ask-only when base-heavy), and the DEFENSIVE breaker caps deployment. Symmetric top-ups are suspended near the hard limits. MM0 never buys inventory back "at any price".
Regimes (src/strategy/regime.ts): CALM < 40%, NORMAL < 90%, VOLATILE < 160%, else STRESSED (σ_eff annualized). Acceleration (σ_short / σ_long > 2.5) bumps the regime one level. A 1-minute reference move > 8% or venue/oracle deviation > 1% → DISLOCATED. External ±2% depth < 40% of its 24h EMA → LIQUIDITY_SHOCK. Escalation is immediate, de-escalation needs 60 minutes of persistence. Every regime carries its numeric reasons.
Multi-range bands (CORE / ACTIVE / TAIL) are deliberately not in V1. They are a Stage 2 experiment, adopted only if walk-forward simulation shows a material improvement in J.
9. Strategy governance
Execution and strategy change are separate. Re-ranging an approved position may happen several times a day. Changing the strategy is slow and goes through four steps:
- Propose: a supervisor recommendation passes
validateSupervisorOutput: strict keys, enum reasons, bounded numbers, andsummarytreated as display-only text. - Validate:
validateStrategyConfigchecks that the proposed config stays inside the mandate box. For risk-increasing changes (narrower width, less reserve, leavingWIDE_DEFENSIVE_CL), a replay gate also runs current vs proposed over the maker's own last 72h of reference prices and requiresJ_proposed ≥ J_current − 0.1% NAV. With no validator configured, risk-increasing changes are rejected. - Publish:
STRATEGY_CHANGE_PROPOSEDgoes to the ledger with direction, reasons and simulation metrics. - Activate: after a timelock (default 60 min),
STRATEGY_CHANGE_ACTIVATEDis recorded.
10. Public surfaces
10.1 Action receipts
Every executed, failed or verification-rejected action produces an ExecutionReceipt with: action number, action, venue, previous and new range, capital, structured reason codes + rationale, inventory before/after, risk result, tx id, vault delta, fees collected, tx cost, model version, strategy version, and mandate hash. No chain-of-thought is ever recorded; rationales are generated from numbers by templates.
10.2 Ledger
EventLedger (src/ledger/ledger.ts) is append-only and hash-chained (hash = sha256(canonical(body ∥ prevHash))). Event types include MAKER_CREATED, MANDATE_PUBLISHED, TOKEN_PREFLIGHT, DEPOSIT, STATE/MODE/REGIME_CHANGED, ACTION_EXECUTED/FAILED/REJECTED, CIRCUIT_BREAKER, INVENTORY_WARNING, SUPERVISOR_*, QUANT_*, STRATEGY_CHANGE_*, EMERGENCY_CONTROL and DAILY_SNAPSHOT. In production the head hash is anchored onchain periodically.
10.3 Maker page (mm0.pro/maker/[token])
This page shows capital, deployed, reserve, state/mode, venue, ±1/2/5% depth with MM0's contribution, fees, net MM P&L with attribution (fees / inventory / costs), realized vs unrealized, vs-HODL, max drawdown, inventory split, active breakers, positions, actions, mandate, and ledger head. A text rendering is produced by makerPage() in src/simulator/report.ts.
10.4 Market Quality Score (specified, not shipped in V1)
If shipped, the score must be decomposable:
| Component | Score (0–100) |
|---|---|
| Depth | 100 · min(1, D₂ / D₂_ref); D₂_ref is set in the mandate |
| Slippage | 100 · clamp(1 − slip₁₀ₖ / slip_ref, 0, 1) |
| Uptime | 100 · time-in-range |
| Inventory health | `100 · (1 − |
| Capital efficiency | 100 · min(1, (fees / deployed-capital-days) / ref) |
| Overall | weighted mean; weights published with the score |
Every input and weight is shown next to the score.
11. Protocol economics
Options, with their incentive analysis:
| Model | Aligned with | Perverse incentive | Verdict |
|---|---|---|---|
| % of legitimate LP fees earned | real fee generation | pushes toward narrower ranges (more fees, more divergence) | acceptable only with mandate caps and net-P&L disclosure |
| Management fee on active (deployed) capital | — | pushes toward over-deployment | reject as the sole fee |
| Management fee on NAV (bps/yr) | neutral | none on behavior, but weak alignment | good base fee |
| Performance fee on net MM P&L vs HODL, high-water mark, flow-adjusted | value created by market making | none known, if defined rigorously | preferred variable fee |
| Per-trade / volume / market-cap / price-based | — | manipulation | prohibited |
Preliminary recommendation: a low NAV-based management fee plus a performance fee on flow-adjusted net market-making P&L versus the HODL benchmark, above a high-water mark. "Performance" means ΔNAV_unitized − ΔHODL, net of fees, costs, realized and unrealized inventory, and adjusted for capital flows. Gross LP fees are never called profit. The final choice is deferred until Stage 2 and 3 data exist.
12. Numerics
The simulator uses float64. Onchain programs must use fixed-point integer math (u64 amounts, Q64.64 prices) with explicit rounding toward the vault on deposits and away from the vault on withdrawals, checked-arithmetic everywhere, and property tests against the float reference model.
13. Development stages
| Stage | Deliverable | Status |
|---|---|---|
| 0 | These five specs; all invariants defined | done (this document set) |
| 1 | Local simulator: synthetic token + bin AMM, vault, strategy, range engine, inventory, risk, accounting, ledger, stress suite | done (npm run sim, npm run stress, npm test) |
| 2 | Historical backtesting on real datasets, walk-forward, overfitting measurement, multi-range experiment | next |
| 3 | Mainnet reads, shadow mode ("what I would have done") | — |
| 4 | Devnet/local-validator execution with test capital via a real venue adapter | — |
| 5 | Audited, small, single-token mainnet pilot with manual pause | — |
| 6 | Cautious expansion | — |
Stages are not skipped because the UI looks good.