# Introduction

Nexus is a blockchain built for programmable finance. Its architecture pairs a general-purpose smart-contract environment with a specialized, high-performance financial execution engine — under one consensus layer and a distributed proof system — so applications get the openness and composability of a public chain with the speed and depth of a centralized exchange.

At its center is the **Nexus Exchange**: a spot and perpetual-futures exchange built into the chain itself, not bolted on as a smart contract. It is **live on testnet today** and expands toward crypto, equities, FX, and commodities as it moves to production.

<figure><img src="/files/yNV3n1XZiLPSVNrSyz4M" alt="The Nexus stack: NexusEVM and NexusCore (with native co-processors) in the execution layer, the verification layer, and NexusBFT consensus, with applications on top."><figcaption><p>The Nexus stack — dual-core execution, verification, and consensus.</p></figcaption></figure>

## How it fits together

Nexus separates execution, verification, and consensus over one unified state:

* **Execution — dual-core.** NexusEVM runs general-purpose, EVM-compatible contracts (1-second blocks); NexusCore runs native financial co-processors — order matching, risk, oracles, liquidations — at 200 ms block intervals. They interoperate atomically, so any contract can compose with exchange liquidity.
* **Verification.** The Compute Network progressively proves execution, so anyone can verify it independently — without trusting the operator.
* **Consensus.** NexusBFT finalizes blocks with instant, single-slot finality.

See [Architecture](/architecture/architecture) for the full picture.

## Start here

* [Architecture](/architecture/architecture) — how the chain and the exchange work.
* [The Exchange](/exchange/introduction) — trading, perpetuals, and markets.
* [Build on Nexus](/network/building-on-nexus) — deploy with standard EVM tools.
* [Tokenomics](/overview/tokenomics) — NEX supply, utility, and distribution.

## Further reading

* [Original Nexus whitepaper](https://assets.nexus.xyz/whitepaper.pdf) — the foundational technical paper that introduced Nexus.
* [NEX MiCA whitepaper](https://assets.nexus.xyz/mica/mica-whitepaper.pdf) — the NEX token disclosure under the EU Markets in Crypto-Assets (MiCA) regulation.


# Economic Model

Nexus is designed as one integrated economic system — a purpose-built blockchain, a verifiable exchange, and a native dollar stablecoin — built to enable powerful financial activity at scale.

## The Nexus Trifecta

Three reinforcing primitives operate as a single economic machine rather than as separate products:

* **The Nexus blockchain** — a purpose-built, EVM-compatible chain with a high-performance trading engine built in and verifiable execution.
* **The Nexus Exchange** *(planned)* — a verifiable spot and perpetual futures venue running directly on the chain.
* **USDX** *(planned)* — a native dollar stablecoin, backed 1:1 by short-duration U.S. Treasury bills and cash equivalents, used as the Exchange's margin and settlement asset.

## Economic engines

Each part of Nexus is an economic engine in its own right, and the three are designed to compound into a single system:

### The Exchange

The Nexus Exchange will use a **maker–taker model** — makers who add liquidity earn rebates, takers who remove it pay fees. Trading fees will be the Exchange's primary economic output, scaling with traded volume and liquidity depth.

### The Stablecoin

USDX is designed to be the Exchange's margin and settlement asset. Dollar balances settled in USDX — rather than a third-party stablecoin such as USDC or USDT — are intended to generate **U.S. Treasury-bill yield** on the backing reserves: an economic flow that scales with the dollar value held and settled through the network.

### The Blockchain

The Nexus blockchain is fully **EVM-compatible and permissionless**, so its economy grows with everything built on it. Builders extend the venue directly — market deployers list new spot and perpetual markets, and frontend operators route order flow, earning a share of the fees they generate. Beyond the Exchange, EVM applications and integrations broaden network usage, and all of this activity consumes blockspace — deepening the chain's base-layer economy.

## How the engines compound

The three engines are not independent — they reinforce one another, so activity in one strengthens the others. This is the core of the model:

```mermaid
flowchart LR
  T[Trading volume] --> U[USDX demand & TVL]
  U --> L[Deeper liquidity]
  L --> E[Better execution]
  E --> T
  B[New markets & apps] --> T
```

* **The Exchange → liquidity.** More trading volume deepens order books and tightens spreads, which improves execution and attracts still more volume.
* **The Stablecoin → capital.** More value settling in USDX grows reserve yield *and* deepens USDX liquidity, which improves margining and settlement — feeding back into trading.
* **The Blockchain → platform.** Permissionless builders and EVM applications add markets, order flow, and usage, broadening coverage and volume — which in turn attracts more builders.

Each loop also consumes blockspace, so growth in any one engine deepens the chain's base-layer economy. **NEX is the unit of account that circulates through these flows** — see [Tokenomics › Utility](/overview/tokenomics) for the token's specific roles.

***

<sub>Forward-looking statement. The Nexus Exchange and USDX are in development and have not yet launched; the features, economics, and revenue described here are planned, are estimates only, are subject to change without notice, and may differ materially from actual outcomes.</sub>


# Tokenomics

NEX is the native token of the Nexus blockchain. This page covers its total supply, utility, distribution status, and vesting schedule.

## Token overview

| Property                          | Value                                                      |
| --------------------------------- | ---------------------------------------------------------- |
| Token name / ticker               | Nexus (NEX)                                                |
| Total supply                      | **100,000,000,000,000 NEX (100T)**                         |
| Token contract (Ethereum, ERC-20) | `0xf57D49646621F563b0B905aFc8336923AC569Ec5` (18 decimals) |
| Genesis / TGE                     | May 20, 2026                                               |

## Utility

NEX is the **native token of the Nexus blockchain** — the asset that powers computation, pays for network operation, and aligns participants across the ecosystem. Each utility is described in detail below.

### Gas for transactions

Every transaction on the Nexus blockchain — value transfers, contract calls, and, in the future, exchange operations — pays its fee in NEX. NEX is the **sole gas asset**: it is required to submit any transaction to the network, so demand for blockspace translates directly into demand for the token. Gas is priced by the computational resources a transaction consumes, so heavier operations cost proportionally more NEX.

### Smart-contract execution

The Nexus blockchain is fully EVM-compatible, and NEX **meters the execution of smart contracts** deployed to it. Each operation a contract performs consumes gas, paid in NEX and priced by the resources used. As programmable activity on the network grows — DeFi, integrations, and applications built by third parties — NEX is the settlement asset that underpins all of it.

### Consensus and compute

NEX is used to **interact with the network's consensus and compute layers** — the systems that order transactions, reach finality, and generate the cryptographic proofs that let anyone verify execution independently. NEX denominates the fees that compensate these operations, tying the token directly to the security, liveness, and verifiability of the network. (NEX is a fee and operating asset; it does not confer staking rewards, dividends, or governance rights — see the note on rights below.)

### Exchange incentives and alignment *(planned)*

Beyond gas, NEX is intended to be the asset that **incentivizes and aligns activity on the Nexus Exchange** — encouraging the behaviors that make a venue liquid and useful. Planned mechanisms include:

* **Trading incentives** — fee discounts are the first and most concrete example: holding NEX is designed to lower taker fees and improve maker rebates, stacking with standard volume-tier pricing.
* **Liquidity and deposits** — incentivizing the capital and order flow (TVL) that deepen markets and improve execution.
* **Builders** — aligning third parties who list markets and route order flow with the growth of the venue.

Fee discounts are planned for the Exchange launch; the broader incentive mechanisms will be documented as they are introduced. See the [Economic Model](/overview/economic-model) for how exchange activity reinforces network usage.

### Future applications

As the network matures, NEX may be used within **additional applications** on the Nexus blockchain as production features are introduced. Any such utility will be documented here as it becomes available.

## Distribution status

![NEX token breakdown — Treasury 60%, Team 20%, Investors 20%; total supply 100,000,000,000,000 NEX (100T).](/files/xVi7sK98FJVaQhqHmWit)

| Allocation | Share | Amount (NEX)       |
| ---------- | ----- | ------------------ |
| Treasury   | 60%   | 60,000,000,000,000 |
| Team       | 20%   | 20,000,000,000,000 |
| Investors  | 20%   | 20,000,000,000,000 |

Early backers include Dragonfly ($2.2M seed, 2022) and Lightspeed and Pantera ($25M Series A, 2024). See [Contributors & Partners](/overview/contributors-and-partners) for the full list.

## Vesting schedule

At genesis, the **60T Treasury is unlocked** and the **40T Team & Investor allocation is locked** on a single schedule: a one-year cliff (May 20, 2027) releases **33%**, followed by monthly linear unlocking over the next 24 months, reaching full supply by May 2029.

| Milestone      | Date         | Unlocked supply |
| -------------- | ------------ | --------------- |
| Genesis (TGE)  | May 20, 2026 | \~60T (60%)     |
| 12-month cliff | May 2027     | \~73T (73%)     |
| Full unlock    | May 2029     | 100T (100%)     |

***

<sub>Note on rights. Consistent with the MiCA whitepaper, NEX does not confer ownership rights, dividend rights, or automatic governance/voting rights. Protocol rules are changed through the network's consensus mechanisms and by validators during protocol upgrades.</sub>

<sub>Forward-looking statement. The unlocked-supply trajectory and all dates and figures are estimates only, subject to change without notice, and may differ materially from actual outcomes. Token release and unlock schedules may be adjusted, delayed, accelerated, or otherwise modified.</sub>


# Roadmap

Key milestones delivered to date, and what is planned next.

## Delivered

* **2022 · Founded.** Nexus begins research into high-performance verifiable computation.
* **Sep 2022 · Seed round.** Led by Dragonfly.
* **May 2024 · Series A.** Co-led by Lightspeed and Pantera.
* **Q1 2026 · Testnet matching engine.** First on-chain order-matching engine live on testnet.
* **May 20, 2026 · Mainnet & TGE.** Mainnet launch, NEX token generation event, and exchange listings.
* **Jun 2026 · Exchange testnet.** The Nexus Exchange opens for trading on testnet, following mainnet launch and TGE.

## Upcoming

* **H2 2026 · USDX.** Launch of the native dollar stablecoin, backed 1:1 by short-duration U.S. Treasury bills and cash equivalents.
* **H2 2026 · Exchange (production).** Production launch of the Nexus Exchange — verifiable spot and perpetual futures.

***

<sub>Forward-looking statement. Roadmap items, timelines, and dates are estimates only, subject to change without notice, and may differ materially from actual outcomes.</sub>


# Contributors & Partners

## Team

The Nexus team brings together deep expertise in financial markets, AI, and large-scale systems engineering — with academic backgrounds from Stanford, UC Berkeley, UCLA, University College London, and Brown, and operational experience across leading technology and finance organizations including Google, Amazon, Microsoft, PwC, and Mozilla.

## Advisors

Nexus is advised by distinguished cryptographers and leaders from institutions including NYU, MIT, Harvard, UC Berkeley, and Cornell, and from the Ethereum Foundation, Zcash, Chainlink, and Bitso.

## Investors

Nexus is backed by leading crypto investors — led by **Dragonfly**, **Lightspeed**, and **Pantera** — alongside Faction, SV Angel, Alliance, Blockchain Builders Fund, and the Stanford Blockchain Accelerator.

## Technology & protocol partners

* **M0** — collaborating on [USDX](/network/building-on-nexus/tokens-and-bridges), the native dollar stablecoin (in development).
* **Hyperlane** — cross-chain [bridging](/network/building-on-nexus/tokens-and-bridges/bridging) for NEX between Ethereum and BSC.

Nexus works with a growing ecosystem of partners across AI, zkVM, and crypto. See [nexus.xyz/ecosystem](https://nexus.xyz/ecosystem) for the current list.

## Build on Nexus

Nexus is permissionless and EVM-compatible, so any team can build on it — and, over time, launch their own markets on the exchange. See [Build on Nexus](/network/building-on-nexus).


# Overview

Nexus is a blockchain built for programmable finance. Its architecture pairs a general-purpose smart-contract environment with a specialized, high-performance financial execution engine, under a single consensus layer and a distributed proof system — so applications get the composability of a public chain and the speed and depth of a centralized exchange.

<figure><img src="/files/yNV3n1XZiLPSVNrSyz4M" alt="The Nexus architecture stack: NexusEVM and NexusCore (with native co-processors) in the Execution layer, the Nexus zkVM verification layer, and NexusBFT consensus, with applications on top."><figcaption><p>The Nexus stack — dual-core execution, verification, and consensus.</p></figcaption></figure>

## The layers

Nexus separates **execution**, **verification**, and **consensus** while keeping a single, unified global state.

| Layer            | Components           | Role                                                                                           |
| ---------------- | -------------------- | ---------------------------------------------------------------------------------------------- |
| **Execution**    | NexusEVM · NexusCore | Runs general-purpose contracts (NexusEVM) and specialized financial co-processors (NexusCore). |
| **Verification** | Compute Network      | Progressively produces validity proofs of execution that anyone can verify independently.      |
| **Consensus**    | NexusBFT             | Orders transactions and finalizes blocks with single-slot finality.                            |

## Dual-core execution

The execution layer runs two synchronized engines:

* **NexusEVM** — an Ethereum-compatible virtual machine for general-purpose, composable smart contracts. It produces 1-second blocks and supports the standard Ethereum toolchain.
* **NexusCore** — a high-performance engine that hosts **enshrined co-processors**: native financial modules (order matching, risk, oracles, liquidations) that run deterministically at 200 ms block intervals. NexusCore powers the Nexus Exchange.

The two engines interoperate through atomic cross-core messaging, so an EVM contract and a NexusCore co-processor can act within a single, consistent transaction. This lets developers combine smart-contract flexibility with exchange-grade financial execution.

## Verification

The **Compute Network** generates validity proofs of execution using the Nexus zkVM and is live on Nexus Testnet. Proof coverage expands incrementally toward both NexusEVM and NexusCore, so any party can verify correctness without re-executing or trusting the operator.

## Consensus

**NexusBFT** is the consensus mechanism, built on CometBFT. Blocks are final when committed — there is no probabilistic finality window or reorgs.

## How execution flows

Today, a developer submits an EVM transaction; NexusEVM executes it and NexusBFT commits the block with single-slot finality. As the Nexus Exchange comes online, a trader will submit a signed order over the API; the sequencer will route it to NexusCore, which will match, update positions, and run risk checks at 200 ms intervals; NexusBFT will commit the block and the Compute Network will prove the batch.

## Design principles

* **Performance** — financial operations execute deterministically at 200 ms; the architecture targets centralized-exchange-grade throughput and latency.
* **Composability** — NexusEVM, NexusCore, and every co-processor interoperate within single transactions.
* **Extensibility** — new co-processors can be added without changing consensus.
* **Verifiability** — execution is provable and independently checkable.

Explore each component: [NexusCore](/architecture/nexuscore) · [NexusEVM](/architecture/nexusevm) · [NexusBFT](/architecture/nexusbft) · [Compute Network](/architecture/compute-network).


# NexusCore

NexusCore is the specialized, high-performance financial execution engine of the Nexus blockchain — the second core alongside [NexusEVM](/architecture/nexusevm). It runs **enshrined co-processors**: native financial modules built directly into the protocol rather than deployed as smart contracts, executing deterministically at 200 ms block intervals.

NexusCore powers the Nexus Exchange. The same co-processor framework is designed to host further financial primitives over time — vaults, oracles, lending, stablecoins, and more.

## The Exchange Kernel

The flagship co-processors form the **Exchange Kernel** — the matching, risk, funding, and liquidation primitives behind the Nexus Exchange:

<figure><img src="/files/1wGTjiahnB7jwVugbTxL" alt="NexusCore hosting the Exchange Kernel — CLOB, margining, risk, and liquidation engines — surrounded by per-market co-processors."><figcaption><p>NexusCore's Exchange Kernel and per-market co-processors.</p></figcaption></figure>

* **Central Limit Order Book (CLOB)** — native order matching with price-time priority.
* **Risk Engine** — margining and real-time position risk.
* **Oracle Engine** — mark and index pricing for every market.
* **Liquidation Engine** — deterministic liquidations and solvency protection.

Because these run in-protocol rather than as contracts, markets execute deterministically and settle with the chain's finality.

## Co-processors and parallelism

Each co-processor is an isolated, purpose-built state machine. They run in parallel — independent modules (matching, oracles, future vaults) execute concurrently, and individual co-processors parallelize internally across markets and accounts. The architecture targets near-linear scaling with validator hardware while preserving deterministic execution.

Co-processors expose **dual interfaces**:

* a **native API** (REST + WebSocket) for direct, low-latency access; and
* an **EVM interface** (precompiles) so NexusEVM contracts can compose with NexusCore — placing orders, reading market state, and managing margin atomically within a single transaction.

## Extensible markets

NexusCore is designed to make markets **permissionless to deploy**: in time, builders will be able to launch a perpetual market for any asset by supplying an oracle — spanning crypto, FX, commodities, equities, indices, and more exotic markets.

<figure><img src="/files/wh5dj2YdhnTpILAofNU8" alt="The Nexus Exchange vision — a CLOB at the center serving perps and spot across many asset classes: equities, FX, stables, indexes, options, commodities, information, and LSTs."><figcaption><p>One liquidity layer across every asset class.</p></figcaption></figure>

## Role in the system

NexusCore and NexusEVM operate as parallel execution domains with different block cadences — 200 ms for financial execution, 1 second for smart contracts. NexusBFT commits blocks for both with single-slot finality, and the [Compute Network](/architecture/compute-network) proves execution so anyone can verify it.


# NexusEVM

NexusEVM is the EVM execution layer of the Nexus blockchain — the general-purpose core alongside [NexusCore](/architecture/nexuscore). It produces 1-second blocks with single-slot finality.

## EVM compatibility

* Same bytecode — no Nexus-specific compiler flags
* Same gas semantics — standard opcodes cost the same gas as on Ethereum
* Standard JSON-RPC API (`eth_sendTransaction`, `eth_call`, etc.)
* Standard toolchains — Solidity, Vyper, Hardhat, Foundry, Remix, OpenZeppelin

## Connect

NEX is the native gas token. Add the network in any EVM wallet:

| Network | Chain ID | RPC                             | Explorer                             |
| ------- | -------- | ------------------------------- | ------------------------------------ |
| Mainnet | 3946     | `https://mainnet.rpc.nexus.xyz` | `https://explorer.nexus.xyz`         |
| Testnet | 3945     | `https://testnet.rpc.nexus.xyz` | `https://testnet.explorer.nexus.xyz` |

See [Building on Nexus](/network/building-on-nexus) and [Endpoints](/network/building-on-nexus/endpoints) to start deploying.

## Developer experience

Deploy using Hardhat, Foundry, or Remix, with standard libraries (OpenZeppelin, ethers.js, viem, web3.py). When the Exchange launches, EVM contracts will be able to call into Exchange functions atomically within a single transaction.

## Role in the system

NexusEVM handles programmable smart-contract execution; NexusCore handles specialized financial execution at a faster cadence. The two layers interoperate, so any EVM contract can compose with NexusCore — and, when the Exchange launches, with Exchange liquidity.


# NexusBFT

NexusBFT is the consensus mechanism of the Nexus blockchain, built on CometBFT.

## Single-slot finality

Blocks are final when committed. There is no probabilistic finality window. Once >2/3 of validators precommit a block, it is irreversible.

## Consensus rounds

1. **Propose** — A designated proposer assembles a block and broadcasts it
2. **Prevote** — Validators evaluate the block and broadcast a prevote (for the block or nil)
3. **Precommit** — Once >2/3 prevotes are seen for the same block, validators broadcast a precommit
4. **Commit** — Once >2/3 precommits are seen, the block is committed and final

If a round fails, the protocol advances to a new round with a new proposer. The system never forks.

## Validators

Validators run a Cosmos consensus node (`nexus-cosmos`) and an EVM execution node (`nexus-evm`).

**Responsibilities:**

* Participate in every consensus round
* Maintain high uptime
* Run both consensus and execution nodes

The validator set is permissioned at launch and will expand over time. Validators will stake NEX to join the active set in subsequent upgrades.

## Role in the system

NexusBFT orders transactions and commits blocks with finality — both NexusEVM blocks and NexusCore financial blocks, at their respective cadences.

After a block is committed, the Compute Network proves the execution batch. NexusBFT provides ordering and finality; the Compute Network provides verifiability.


# Compute Network

The Compute Network is what lets anyone verify Nexus markets without trusting the operator: it produces validity proofs of execution that any party can check independently. It is a distributed proof-generation network, live on Nexus Testnet since December 2024.

While the Nexus blockchain itself is live on mainnet today, the Compute Network remains in active testing: it is exercising and hardening its ability to prove Nexus blockchain blocks on testnet before that proving becomes part of mainnet operation. The components below describe the proving architecture as it is being proven out.

## Decoupled by design

Consensus and verification run as independent networks connected by a deterministic interface. NexusBFT finalizes and orders blocks; the Compute Network proves them. Separating the two lets proving scale and use specialized hardware without adding load to validators, and a proving delay never affects consensus liveness. Each layer scales and upgrades on its own.

## Proof lifecycle

```mermaid
flowchart LR
  F[Block finalized] --> A[Prover assigned]
  A --> P[Proof generated]
  P --> G[Aggregated]
  G --> V[Verified on-chain]
```

Finalized blocks are assigned to provers, which generate validity proofs. Proofs are recursively aggregated into a single succinct proof and verified on-chain, at which point the state is proven. The pipeline runs continuously, overlapping block production for near-real-time verifiability.

## Roles

* **Provers** — generate validity proofs for NexusCore and NexusEVM execution.
* **Aggregators** — recursively combine block proofs into compact epoch proofs.
* **Verifier** — the consensus-side check that validates aggregated proofs and commits the proven state.

## The zkVM

Every node in the network runs the **Nexus zkVM**, the verifiable processor that executes and proves Nexus computation. Its design is published in the [Nexus zkVM specification](https://specification.nexus.xyz/).

## Incentives

The Compute Network has no staking. Nodes are incentivized for the compute they contribute, which the network tracks continuously and aggregates into points. NEX-based incentives for compute activity are distributed on an ad-hoc basis; the first such distribution took place at mainnet genesis.

## Coverage

Proof coverage expands incrementally toward full coverage of both NexusCore and NexusEVM.


# Verifiable Computation

Every state transition on the Nexus blockchain can be checked with cryptographic proofs rather than trusted. That verifiability is produced by the **Nexus zkVM** — a virtual machine that generates succinct proofs attesting that a computation was executed correctly — together with a distributed proving network that produces and aggregates those proofs.

## Why it matters for the Exchange

The Exchange blockchain settles trades, margin, and liquidations on-chain. Verifiable computation lets anyone confirm that execution followed the rules — order matching, settlement, risk — without trusting the operator. Proof coverage expands incrementally as the network matures; the Nexus zkVM is one of several provers that can attest to mainnet execution.

## Proven at scale

This is not aspirational infrastructure. Nexus has operated proving infrastructure at public testnet scale since December 2024, backed by a distributed prover network and academic-grade specifications.

## Learn more

The Nexus zkVM has its own documentation space covering its architecture, SDK, and full specification:

* [Nexus zkVM documentation](https://docs.nexus.xyz/zkvm/overview/the-nexus-zkvm)


# Introduction

In Development

The Nexus Exchange is a verifiable perpetual futures exchange built directly into the Nexus blockchain. The order book, matching engine, and risk system run as native protocol components — not as a smart contract on a general-purpose chain — so the exchange settles at CEX-class speed while every fill, funding payment, and liquidation remains independently verifiable.

### What's live today

The exchange is **live on Nexus Testnet** as a development preview. You can connect a wallet, create API keys, fund a synthetic-USDX account from the faucet, and trade the live perpetual futures markets programmatically over REST and WebSocket.

* **4 perpetual markets live today** — BTC, ETH, SOL, and NDQ, all collateralized and quoted in synthetic USDX. The configured set expands to 32 — additional crypto, FX, commodities, and equity indices — across upcoming releases.
* **Leverage is configurable per market.**
* **API-first.** All trading happens over the REST and WebSocket APIs. The web frontend at [exchange.nexus.xyz](https://exchange.nexus.xyz) is a read-only market and portfolio view — order entry is via the API.
* **Non-custodial by design.** Balances and collateral are held by the exchange protocol, never by a private operator.
* **Synthetic test funds.** Each API key can claim test USDX from the faucet to trade against live, bot-seeded liquidity.

> This is a development preview on testnet. It is not a regulated venue and not an offer or solicitation. Markets are seeded with automated liquidity for testing; prices and balances have no real-world value.

### What's coming

Perpetual futures are the first market type. Spot, additional derivatives, and broader asset classes — equities, FX, commodities, RWAs — follow in later releases. The roadmap progresses through a sequence of release gates, each adding robustness, risk controls, and on-chain settlement guarantees before the next.


# Architecture

The Nexus Exchange runs a high-performance order book and matching engine, with execution settled on-chain and independently verifiable. It is live on Nexus Testnet today as a development preview; the production launch follows in a later release.

### Nexus Exchange components

* **Execution** — two synchronized execution environments process all transactions and state transitions:
  * **NexusCore** — the high-performance engine that hosts *enshrined co-processors* for specialized financial operations, with deterministic execution at 200 ms block intervals.
  * **NexusEVM** — an Ethereum-compatible virtual machine providing general-purpose programmability, composability, and full EVM tooling.
* **Consensus** — **NexusBFT**, a Byzantine Fault Tolerant protocol that finalizes dual-execution blocks, coordinates validators, and manages the on-chain registry of co-processors.
* **Verification** — the **Compute Network** progressively proves execution, so anyone can verify it independently without trusting the operator.

Together these let developers build and access institutional-grade financial infrastructure on-chain — with 200 ms block intervals, shared native liquidity, and verifiable execution — bringing the speed, depth, and reliability of traditional markets into an open, composable blockchain environment.


# Trading

The Nexus Exchange lets users trade on a central-limit order book (CLOB) on the Nexus blockchain. Orders are matched on NexusCore at 200 ms block intervals with instant finality. Trading is live on Nexus Testnet today; the production launch follows in a later release.

| Property        | Value                                                        |
| --------------- | ------------------------------------------------------------ |
| Execution       | 200 ms blocks, price-time priority CLOB                      |
| Finality        | Instant (NexusBFT)                                           |
| Custody         | Non-custodial — assets held on-chain                         |
| Margin currency | Synthetic USDX on testnet; USDX at production launch         |
| Verifiability   | Trades are progressively proven and independently verifiable |


# Quickstart

This walks you from zero to a live order on testnet. You need an Ethereum wallet to sign the login message. All requests go to the testnet gateway:

```
https://exchange.nexus.xyz/api/exchange
```

Monetary values are decimal **strings** throughout the API.

### 1. Sign in

Authenticate with an EIP-191 personal signature over the fixed string `Sign in to Nexus Exchange`. Your address is recovered from the signature. The session token is only used to manage API keys — not to trade — and expires in 24 hours.

```bash
curl -X POST 'https://exchange.nexus.xyz/api/exchange/auth/login' \
  -H 'Content-Type: application/json' \
  -d '{
    "message": "Sign in to Nexus Exchange",
    "signature": "0xSIGNATURE_HEX"
  }'
# → { "token": "sess_abc123...", "address": "0xYOUR_WALLET_ADDRESS" }
```

### 2. Create an API key

Use the session token to mint an HMAC key pair. **The secret is shown once — save it immediately.** Keys inherit your account's rate-limit tier.

```bash
curl -X POST 'https://exchange.nexus.xyz/api/exchange/keys' \
  -H 'Authorization: Bearer sess_abc123...' \
  -H 'Content-Type: application/json' \
  -d '{"label": "my-bot"}'
# → { "key_id": "nx_7f3a1b...", "secret": "e4d2c8f1...long_hex..." }
```

### 3. Sign requests

Every authenticated request carries three headers. Build a canonical string, HMAC-SHA256 it with your secret, and attach the result.

| Header        | Value                                                               |
| ------------- | ------------------------------------------------------------------- |
| `X-API-Key`   | your `key_id` (`nx_...`)                                            |
| `X-Timestamp` | current time in ms since epoch (must be within ±30s of server time) |
| `X-Signature` | HMAC-SHA256 hex of the canonical string                             |

Canonical string format (note the literal newlines):

```
timestamp\nMETHOD\npath\nquery\nsha256(body)
```

For a GET with no body, hash the empty string.

```bash
TIMESTAMP=$(date +%s%3N)
BODY_HASH=$(echo -n "" | shasum -a 256 | cut -d' ' -f1)
CANONICAL="$TIMESTAMP\nGET\n/markets\n\n$BODY_HASH"
SIGNATURE=$(echo -ne "$CANONICAL" | openssl dgst -sha256 -hmac "YOUR_SECRET" | cut -d' ' -f2)

curl 'https://exchange.nexus.xyz/api/exchange/markets' \
  -H "X-API-Key: nx_7f3a1b..." \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Signature: $SIGNATURE"
```

### 4. Fund your account

Credit synthetic USDX from the faucet. Each API key can claim up to **500 USDX per day**; omit `amount` to claim the full remaining daily allowance. Returns `429` once the allowance is used up; it resets the next UTC day.

```bash
curl -X POST 'https://exchange.nexus.xyz/api/exchange/account/credit' \
  -H 'X-API-Key: nx_7f3a1b...' -H 'X-Timestamp: UNIX_MS' -H 'X-Signature: HMAC_HEX' \
  -H 'Content-Type: application/json' \
  -d '{"amount": "500"}'
# → { "amount": "500", "credited_today": "500", "daily_limit": "500" }
```

### 5. Browse markets

```bash
# All live markets
curl 'https://exchange.nexus.xyz/api/exchange/markets' -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'

# A single ticker
curl 'https://exchange.nexus.xyz/api/exchange/markets/BTC-USDX-PERP/ticker' -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'
# → { "symbol": "BTC-USDX-PERP", "last": "84250.5", "bid": "84249.0", "ask": "84252.0", "volume": "1523.4", "change": "2.1" }
```

### 6. Place an order

```bash
curl -X POST 'https://exchange.nexus.xyz/api/exchange/orders' \
  -H 'X-API-Key: nx_7f3a1b...' -H 'X-Timestamp: UNIX_MS' -H 'X-Signature: HMAC_HEX' \
  -H 'Content-Type: application/json' \
  -d '{
    "market_id": "BTC-USDX-PERP",
    "side": "Buy",
    "order_type": "Limit",
    "price": "83000.00",
    "quantity": "0.01",
    "time_in_force": "GTC"
  }'
# → { "id": "ord_f82a...", "status": "open", "filled": "0.0", ... }
```

Use `"order_type": "Market"` to fill immediately at the best available price. `time_in_force` accepts `GTC`, `IOC`, or `FOK`; add `"reduce_only": true` to only reduce an existing position. Batch multiple orders in one `POST /orders/batch` call — they are processed sequentially and results preserve request order.

### 7. Monitor positions

```bash
curl 'https://exchange.nexus.xyz/api/exchange/positions' -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'
curl 'https://exchange.nexus.xyz/api/exchange/account'   -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'
# → { "balance": "500.0", "equity": "512.5", "margin_used": "83.0", "margin_ratio": "0.008", "positions": 1 }
```

Next: the full REST API Reference and WebSocket API Reference.


# Perpetuals

Perpetual futures are the first market type on the Nexus Exchange and are **live on testnet** across 4 markets today (BTC, ETH, SOL, NDQ), expanding to 32. A perpetual is a leveraged long or short position with no expiry; it tracks an underlying index price through a periodic funding payment between longs and shorts.

Perpetuals are implemented natively in the exchange engine — matching, margin checks, funding, and liquidation all run in-protocol and settle deterministically, rather than through a smart contract on a general-purpose VM.

### Market structure

* **4 markets live today** — BTC, ETH, SOL, NDQ — all quoted and collateralized in synthetic USDX. Expanding to 32.
* Planned categories as the set expands: major crypto, altcoins, FX, commodities, and equity indices.
* Each market has its own contract specification — tick size, minimum/maximum order size, leverage, maintenance margin rate, funding interval, and open-interest cap. See Market Specifications for the full table.

### How a position works

1. **Fund collateral.** Deposit (or, on testnet, faucet) synthetic USDX into your account. Collateral is shared across all positions (cross-margin).
2. **Open a position.** Submit a buy (long) or sell (short) order via the API. The engine checks initial margin atomically before accepting the order — see Margining.
3. **Hold.** Your position accrues unrealized PnL as the mark price moves and pays or receives funding each interval.
4. **Close or get liquidated.** Close by submitting an opposing order. If your account equity falls below maintenance margin, the position is liquidated.

### Pricing

Each market has a **mark price** derived from an external index feed, used for margin, funding, and liquidation. Mark price is independent of the last trade price on the book, which prevents thin-book trades from triggering unfair liquidations. See Price Oracles.

### Leverage and margin

Leverage is configurable per market. Margin is whole-account (cross): your entire USDX balance backs all open positions. The leverage and maintenance margin rate for each market are listed in Market Specifications. See Margining for how initial and maintenance margin are calculated.

### Sub-pages

* Margining
* Order Types
* Positions
* Funding Rates
* Liquidations
* Price Oracles


# Margining

Margin is enforced **natively inside the exchange engine** and verified atomically before any order is accepted. Margining is **whole-account (cross)**: your entire USDX balance is the collateral pool backing every open position. Per-market isolated margin is planned for a later release.

### Definitions

| Term                   | Meaning                                                                             |
| ---------------------- | ----------------------------------------------------------------------------------- |
| **Notional**           | `position_size × mark_price` — the value of the position at the current mark price. |
| **Equity**             | Account balance + unrealized PnL across all positions.                              |
| **Initial margin**     | Collateral required to *open or increase* a position.                               |
| **Maintenance margin** | Minimum collateral required to *keep* a position open.                              |

### Initial margin

Initial margin is the notional scaled by the market's initial margin rate:

```
initial_margin = position_size × mark_price × initial_margin_rate
```

Equivalently, `initial_margin_rate = 1 / max_leverage`. The engine sums the initial margin required across all positions and rejects an order if your equity cannot cover it.

### Maintenance margin

Each market defines its own **maintenance margin rate** — a configured fraction of notional, not a value derived from leverage at request time:

```
maintenance_margin = position_size × mark_price × maintenance_margin_rate
```

The maintenance margin rate is always **less than or equal to** the initial margin rate (the engine rejects any configuration where maintenance exceeds initial). A position becomes eligible for liquidation when account equity falls below the total maintenance margin across all positions:

```
liquidatable when:  equity < Σ maintenance_margin
```

> **Current testnet values.** Every market currently runs a deliberate **flat 2% margin profile** — `initial_margin_rate = maintenance_margin_rate = 0.02`. These rates are per-market configurable and will be tuned per market in later gates. See Market Specifications for the live per-market values.

### Why mark price, not last price

All margin calculations use the mark price — an external index price — rather than the last trade on the book. This prevents a single thin-book trade from pushing an account into liquidation.


# Order Types

Orders are instructions to buy or sell assets at specified conditions. They define what, when, and how trades should execute on the exchange.

The exchange supports a range of order types and configurations.

### Available today

* **Market** — executes immediately against resting liquidity; any unfilled size is canceled.
* **Limit** — placed at a specific price and stays on the order book until filled or cancelled; fills at the selected limit price or better.
* **Time-in-force** for limit orders:
  * **GTC (Good-Till-Cancelled)** — stays live until filled or cancelled.
  * **IOC (Immediate-or-Cancel)** — fills what it can instantly, cancels the rest.
  * **FOK (Fill-or-Kill)** — fills the entire order immediately or cancels it.
* **Reduce-only** — set `reduce_only: true` on an order so it can only decrease your current position, never flip or increase it.

### Planned

* **GTD (Good-Till-Date)** — stays live until a chosen expiry or until the order fills.
* **Stop orders:**
  * **Stop Market** — a market order triggered when the mark price reaches the selected trigger price.
  * **Stop Limit** — a limit order that becomes active at the selected limit price once the mark price reaches the trigger price.
* **Market-close orders** — sized automatically to fully close your current position.


# Positions

Positions represent your active exposure in markets on the Nexus Exchange. Each position maintains a live state in the exchange's risk engine, tracking your entry price, size, collateral usage, funding, fees, and real-time profit and loss.

Positions update every block, giving near-instant feedback on risk, margin, and liquidation thresholds.

### Open positions

Your open-positions view shows continuously updated metrics from the real-time risk engine:

* **Unrealized PnL** — shown in both USDX value and percentage
* **Position Size** — total notional exposure (long or short)
* **Average Entry Price** — volume-weighted entry across fills
* **Margin Usage** — collateral allocated to maintain the position
* **Liquidation Price** — the estimated price at which the risk engine liquidates your position
* **Funding Payments** — funding paid or received since opening

All calculations follow the deterministic margin and funding rules enforced inside the exchange engine.

### Modifying positions

Active positions are managed through several methods to adjust exposure or exit trades. Updates are applied atomically by the matching engine.

* **Adjusting Position Size**
  * You can increase or decrease your position size via market or limit orders using the standard trading modal.
  * Orders in the opposite direction exceeding your current position size will automatically "flip" your positions.
    * For example, if you have 1 BTC active long and place a market sell for 2 BTC, you'll end up with 1 BTC active short.
* **Closing Positions**
  * Exit a position partially or in full from the Positions tab.
  * Closing executes a market or limit order on the opposite side and immediately realizes PnL after the fill.
* **Position History**
  * Track and review trading activity with transparent historical data through the Position History tab.


# Funding Rates

Funding keeps a perpetual's price anchored to its underlying index. At each funding interval, longs and shorts exchange a payment based on the gap between the perpetual's mark price and the index price. There is no expiry and no central counterparty — funding flows directly between position holders and nets to zero.

### Mechanics

* **Interval.** Funding settles **hourly** (`funding_interval_s = 3600`). Per-market configurable.
* **Premium sampling.** The premium between mark and index is sampled every **60 seconds** (`funding_sample_interval_seconds = 60`) and time-weighted (TWAP) across the interval.
* **Direction.** When the perpetual trades above the index, the funding rate is positive and longs pay shorts; when it trades below, shorts pay longs.
* **Settlement.** At each hourly interval boundary, funding is applied to every open position. The sum across all positions is zero (no value is created or destroyed by funding).

### Per-window cap

Each market caps how large a single funding payment can be, to bound risk during volatile periods:

```
funding_rate_cap = 0.001   (0.1% per window, current testnet value)
```

The cap is a per-market parameter and is relaxed across release gates as the system is validated under wider conditions.

### Payment

For an open position, the funding payment over an interval is:

```
funding_payment = position_notional × funding_rate
```

charged against (or credited to) account equity at settlement. Funding is independent of unrealized PnL — you can pay funding on a profitable position or receive it on a losing one.

> **Status:** funding is live on testnet with hourly settlement and the parameters above. Per-market rates and caps are configurable and subject to tuning between gates. See Market Specifications for current per-market values.


# Liquidations

Liquidations protect the Nexus Exchange from insolvency by ensuring traders maintain sufficient margin to support their open positions.

When an account's equity falls below its required maintenance margin, the exchange's liquidation engine automatically closes positions at the current mark price, preventing negative balances and preserving system-wide solvency.

### Mark price and equity

The exchange uses a mark price derived from the oracle (see [Price Oracles](/exchange/trading/perpetuals/price-oracles)). Account equity is:

```
equity = collateral + unrealized_pnl(mark_price)
```

### Margin requirements

Margin follows the same rules as [Margining](/exchange/trading/perpetuals/margining) — notional valued at the mark price, scaled by each market's configured rates:

```
initial_margin     = position_size × mark_price × initial_margin_rate
maintenance_margin = position_size × mark_price × maintenance_margin_rate
```

### Liquidation trigger

A position becomes eligible for liquidation when account equity falls below the total maintenance margin across all positions:

```
liquidatable when:  equity < Σ maintenance_margin
```

### Liquidation process

1. **Trigger** — equity falls below maintenance margin.
2. **Execution** — a dedicated on-chain liquidator sub-account closes the position at or near the mark price.
3. **Bankruptcy protection** — a price cap prevents negative balances.
4. **Insurance fund** — covers any remaining shortfall if a position cannot be closed in time.


# Price Oracles

Every perpetual market is priced by an external oracle that produces a **mark price** and **index price**. These drive margin, funding, and liquidation — they are deliberately decoupled from the last trade on the order book so that thin-book activity cannot distort risk calculations.

### Sources

The oracle aggregates external reference prices for every live market (4 today, expanding to 32):

* **Hyperliquid** index feeds, polled approximately **once per second**, are the primary source for the markets they cover (BTC, ETH, SOL).
* **Pyth** provides coverage where a Hyperliquid feed is not available (e.g. NDQ).

Each market's mark price is published into the engine through an internal oracle service; the engine itself never makes outbound network calls on the trading path.

### Freshness and deviation guards

Each market is configured with two protective thresholds (per-market, in the engine config):

| Guard         | Current testnet value                    | Behavior                                                                     |
| ------------- | ---------------------------------------- | ---------------------------------------------------------------------------- |
| **Staleness** | `oracle_staleness_seconds = 30`          | A feed older than the threshold is treated as stale.                         |
| **Deviation** | `oracle_deviation_threshold = 0.1` (10%) | A single update that jumps more than the threshold is rejected as anomalous. |

A monotonic timestamp guard (`ts ≥ last_update_time`) rejects out-of-order updates, so a delayed packet cannot overwrite a newer price.

### Fail-closed direction

The oracle is being hardened to **fail closed**: when a feed is stale or fails its guards, the safe behavior is to pause new risk-increasing actions in that market rather than trade on a suspect price. Staleness handling and multi-source fallback are being strengthened across the current release gates.

> **Status:** oracle integration is live on testnet with the guards above. Formal multi-source aggregation and circuit-breaker policy are still being finalized; values and behavior may change between gates.


# Spot

In Development

Spot markets are planned. Perpetual futures are the first market type on the Nexus Exchange; spot trading, settled on the same central-limit order book and shared liquidity, follows in a later release. See the [Roadmap](/overview/roadmap).


# APIs & Rates

The Nexus Exchange is API-first. Everything a trader can do — fund an account, place and cancel orders, query positions, stream the order book — is available over REST and WebSocket. The web frontend is a read-only view; programmatic access is the primary interface.

### Base URL

```
https://exchange.nexus.xyz/api/exchange
```

This is the testnet preview gateway. The machine-readable OpenAPI specification is served at `/openapi.json` and versioned at [`nexus-xyz/nexus-exchange-api`](https://github.com/nexus-xyz/nexus-exchange-api); releases are tracked on [GitHub Releases](https://github.com/nexus-xyz/nexus-exchange-api/releases).

### Authentication model

There are two credential types:

1. **Session token (Bearer).** Created by signing a fixed message with your Ethereum wallet (EIP-191). Used **only** to create and manage API keys — never for trading. Expires after 24 hours.
2. **API key (HMAC).** A `key_id` + `secret` pair. Every trading and account request is signed with HMAC-SHA256 over a canonical request string. The secret is shown once at creation and cannot be retrieved later.

```
1. POST /auth/login   (wallet signature)        → session Bearer token
2. POST /keys         (Bearer)                   → API key_id + secret
3. sign each request  (HMAC over canonical str)  → trade
```

See the Quickstart for the full flow with runnable cURL.

### CCXT

CCXT-compatible **read** endpoints are available today (markets, tickers, order book, trades, balances, positions), tagged in the OpenAPI spec. Broader CCXT support, including order placement, is planned.

### Reference

* Quickstart: Your First Trade
* REST API Reference
* WebSocket API Reference
* Rate & Connection Limits
* [API Versioning](/exchange/apis-and-rates/api-versioning)

> **Status:** testnet preview. Credentials, sessions, and rate-limit state are not yet durable across gateway restarts — treat API keys as re-creatable. Production hardening is in progress.


# Exchange REST

Base URL: `https://exchange.nexus.xyz/api/exchange`

All routes **except** `/health`, `/openapi.json`, and `/auth/login` require HMAC authentication (see Quickstart → Sign requests). The complete machine-readable schema, including request/response bodies and CCXT method mappings, is at `/openapi.json`.

Conventions:

* All monetary values are decimal **strings**.
* On single-order routes, `market_id` is **required** as a query parameter — the engine routes directly to the owning market.
* Authenticated requests carry `X-API-Key`, `X-Timestamp`, `X-Signature`. The timestamp must be within ±30s of server time.

### Authentication & keys

| Method | Path          | Auth    | Description                                                     |
| ------ | ------------- | ------- | --------------------------------------------------------------- |
| POST   | `/auth/login` | —       | EIP-191 wallet signature → 24h session Bearer token             |
| POST   | `/keys`       | Session | Create an HMAC API key (`key_id` + `secret`; secret shown once) |
| GET    | `/keys`       | Session | List your API keys                                              |
| DELETE | `/keys/{id}`  | Session | Revoke an API key                                               |
| POST   | `/ws-tokens`  | HMAC    | Mint a 60s single-use token for a WebSocket subscription        |

### Account

| Method | Path                                        | Auth | Description                                                           |
| ------ | ------------------------------------------- | ---- | --------------------------------------------------------------------- |
| GET    | `/account`                                  | HMAC | Balance, equity, margin used, margin ratio                            |
| GET    | `/account/summary`                          | HMAC | Portfolio aggregates plus the withdrawable balance                    |
| GET    | `/account/state`                            | HMAC | Portfolio summary **and** all open positions from one coherent read   |
| GET    | `/account/fees`                             | HMAC | Effective maker/taker fee schedule for the account                    |
| GET    | `/account/portfolio-history?window=&limit=` | HMAC | Equity / cumulative PnL / cumulative volume time-series               |
| GET    | `/positions`                                | HMAC | Open positions with unrealized PnL and per-position risk detail       |
| POST   | `/account/credit`                           | HMAC | Faucet synthetic USDX (testnet; up to 500/day per key)                |
| GET    | `/account/{address}/adl-history?limit=`     | HMAC | Auto-deleveraging events where the account was target or counterparty |

The portfolio routes and the enriched position fields are documented in full — including the nullable-field convention, the `funding_paid` sign, and why `/account/state` should be preferred over two separate calls — in [Portfolio & Account State](/interfaces/portfolio).

### Orders

| Method | Path                      | Auth | Description                                                        |
| ------ | ------------------------- | ---- | ------------------------------------------------------------------ |
| POST   | `/orders`                 | HMAC | Place a limit or market order                                      |
| POST   | `/orders/batch`           | HMAC | Place multiple orders (sequential; results preserve request order) |
| GET    | `/orders`                 | HMAC | List open orders                                                   |
| GET    | `/orders/{id}?market_id=` | HMAC | Get a single order                                                 |
| PATCH  | `/orders/{id}?market_id=` | HMAC | Amend an order                                                     |
| DELETE | `/orders/{id}?market_id=` | HMAC | Cancel a single order                                              |
| DELETE | `/orders?market_id=`      | HMAC | Cancel all open orders in a market                                 |

Order body fields: `market_id`, `side` (`Buy`/`Sell`), `order_type` (`Limit`/`Market`), `quantity`, `price` (limit only), `time_in_force` (`GTC`/`IOC`/`FOK`), and optional `reduce_only`.

### Market data

| Method | Path                              | Description                                            |
| ------ | --------------------------------- | ------------------------------------------------------ |
| GET    | `/markets`                        | List all live markets and their status                 |
| GET    | `/markets/summary`                | Mark price, 24h volume, trade count, status per market |
| GET    | `/markets/{id}/ticker`            | Last / bid / ask / volume / change                     |
| GET    | `/markets/{id}/orderbook`         | L2 order book depth                                    |
| GET    | `/markets/{id}/trades`            | Recent trades                                          |
| GET    | `/markets/{id}/candles`           | OHLCV candles (1m / 5m / 1h)                           |
| GET    | `/markets/{id}/funding`           | Funding rate history                                   |
| GET    | `/markets/{id}/mark-price`        | Current mark price                                     |
| GET    | `/markets/{id}/status`            | Active/halted, reason, timestamp, ADL count            |
| GET    | `/markets/{id}/adl-events?limit=` | Per-market auto-deleveraging history                   |
| GET    | `/tickers`                        | All tickers in one call                                |
| GET    | `/stats`, `/stats/history`        | Exchange-wide stats                                    |

### Streaming

Real-time order book, trade, and account updates are delivered over WebSocket, not REST. See the WebSocket API Reference.

> **Status:** testnet preview. Endpoint set tracks the versioned OpenAPI spec at [`nexus-xyz/nexus-exchange-api`](https://github.com/nexus-xyz/nexus-exchange-api); consult `/openapi.json` for the authoritative, current schema.


# Exchange Websocket

The WebSocket API delivers real-time order book updates, trades, candles, engine status, and account events (orders, fills, positions, balances, liquidations). It is the recommended path for any latency-sensitive integration.

### Connecting

Browsers cannot attach custom auth headers to a WebSocket upgrade, so authentication uses a short-lived token rather than a header:

1. Call `POST /ws-tokens` over HMAC (a normal signed REST request). This mints a **60-second, single-use** opaque token. Your HMAC secret never crosses the WebSocket boundary.
2. Open a WebSocket to the `/stream` endpoint and present the token to subscribe.

Tokens are single-use and reaped after 60 seconds; mint a fresh one per connection.

### Connection limits

* Maximum **5 active connections per IP**. A 6th connection is rejected (or the oldest is evicted).
* Rate limits and tiers are shared with the REST API — see Rate & Connection Limits.

### Channels

Subscriptions are per-market where noted. Available channels:

| Channel        | Scope      | Payload                      |
| -------------- | ---------- | ---------------------------- |
| `book`         | per market | Order book depth updates     |
| `trades`       | per market | Executed trades              |
| `candles`      | per market | OHLCV candles                |
| `engine`       | global     | Engine throughput and status |
| `orders`       | account    | Order lifecycle updates      |
| `fills`        | account    | Your executed fills          |
| `positions`    | account    | Position changes             |
| `balances`     | account    | Balance changes              |
| `liquidations` | account    | Liquidation events           |

Account-scoped channels (`orders`, `fills`, `positions`, `balances`, `liquidations`) require an authenticated subscription tied to your token.

### Delivery targets

The system targets low-latency fan-out; published latency goals tighten across release gates (order-book and fill delivery measured from the matching event to client receipt). Current testnet behavior is suitable for development and integration testing.

> **Status:** testnet preview. Exact subscription message framing and per-channel schemas are defined in the OpenAPI spec at `/openapi.json` and the live API reference at [exchange.nexus.xyz](https://exchange.nexus.xyz); use those as the authoritative source for message shapes.


# Rate-Limits

The gateway enforces per-API-key rate limits with a token-bucket limiter, plus a per-IP request limit and a WebSocket connection cap. Limits are tiered to support both general programmatic use and high-throughput market-making.

### Request rate (per API key)

| Tier         | Request limit | WebSocket connections | Assignment           |
| ------------ | ------------- | --------------------- | -------------------- |
| Pro          | 20 req/s      | 5                     | Default for all keys |
| Market Maker | 2,000 req/s   | 100                   | Admin-assigned       |

A per-IP limit of **50 req/s** applies in addition to the per-key limit.

When you exceed your limit, the gateway returns **HTTP 429** with a `Retry-After` header. Every response also carries `X-RateLimit-*` headers reporting your tier's ceiling and current usage, so you can pace clients without tripping the limiter.

### WebSocket connections

* **Pro keys: up to 5** active WebSocket connections; **Market Maker: up to 100**.
* Connections beyond the cap are rejected.

### Other limits

| Limit                      | Value                          |
| -------------------------- | ------------------------------ |
| HMAC timestamp window      | ±30s of server time            |
| WebSocket token lifetime   | 60s, single-use                |
| Session token lifetime     | 24h                            |
| Faucet (`/account/credit`) | 500 USDX/day per key (testnet) |

### Designing for the limits

* Batch order placement with `POST /orders/batch` instead of issuing many single `POST /orders` calls.
* Prefer WebSocket streams over REST polling for order book and trade data — it is both faster and far cheaper against your rate budget.
* Cache market metadata (`GET /markets`) rather than re-fetching it on every cycle.
* Pace from the `X-RateLimit-*` headers rather than retrying blindly after a 429.

> **Status:** testnet preview. Tier ceilings are configurable and may change as the tier system is finalized across release gates. Rate-limit state is currently in-memory at the gateway and resets on restart.


# Market Maker Guide

Everything a market maker needs to integrate with the Nexus Exchange. The exchange is API-first — there is no MM-specific UI; you quote, cancel, and stream over the same REST + WebSocket surface every client uses, with a higher rate tier and a maker-only order type.

For the primitives, this guide points at the reference pages rather than restating them: [REST](/exchange/apis-and-rates/exchange-rest), [WebSocket](/exchange/apis-and-rates/exchange-websocket), [Rate & Connection Limits](/exchange/apis-and-rates/rate-limits), and the [Quickstart](/exchange/trading/quickstart).

## 1. Get MM-tier access

The **Market Maker** rate tier (**2,000 req/s** per key) is admin-assigned, not self-serve — see [Rate & Connection Limits](/exchange/apis-and-rates/rate-limits). Request it via your Nexus contact with your `key_id`. Until promoted, a key runs at the base tier. MM keys also get a higher WebSocket connection cap (100 simultaneous connections vs. 5 at base).

> Testnet-preview note: tier assignment and rate-limit state are currently in-memory at the gateway and reset on restart — after a gateway redeploy an MM key may briefly fall back to base until re-promoted.

## 2. Authenticate

Standard flow (details in the [section overview](/exchange/apis-and-rates)): wallet-signed session token (EIP-191) to create an **HMAC API key**, then sign every request with the key. The session token is for key management only — never for trading. Keep the HMAC secret safe; it's shown once.

## 3. The maker order surface

The order type that matters most for market making:

* **Post-only** — set `time_in_force: "PostOnly"` on a limit order. The engine **rejects** the order (rather than filling) if it would cross the book and take liquidity on entry, guaranteeing you rest as a maker. A crossing post-only order comes back as an `InvalidOrder` with code **`WouldTakeLiquidity`** — detect that and re-quote. This is the primary tool for never paying taker fees.
* Standard `GTC` / `IOC` / `FOK` are also available for the non-resting cases.
* **Batch + cancel-all.** Use batch order placement to refresh a quote ladder in one request, and market-scoped cancel-all to pull quotes on a market quickly. Amend is available for in-place price/size changes. (See [REST reference](/exchange/apis-and-rates/exchange-rest).)

## 4. Stream the market

Subscribe over [WebSocket](/exchange/apis-and-rates/exchange-websocket) rather than polling:

* **Order book** and **trades** for the market data you quote against.
* **Your fills / orders / positions** (private channels) to track quote state — mint a single-use ws-token via `POST /ws/token`, then connect.

Connection and subscription caps are per-tier (MM: 100 connections); see [Rate & Connection Limits](/exchange/apis-and-rates/rate-limits). Design for reconnect: the client should re-subscribe and re-sync book state on reconnect.

## 5. What to expect on latency & throughput

Measured on the current testnet preview (experimental, not an SLA):

* Order-to-ack \~**5 ms** p95, WebSocket delivery ≤ **50 ms** p95 under sustained market-making load.
* The MM tier sustains **2,000 req/s** per key; a sustained-load run at 50 req/s held p95 well under the ack target with zero rejects. Pace against the `X-RateLimit-*` headers on every response and back off on `429` + `Retry-After`.

## 6. Tooling

* **SDKs:** Rust (`nexus-exchange`, published to crates.io), Python (`nexus-exchange-py`), and TypeScript (`nexus-exchange-ts`) all wrap this API, including request signing.
* **CLI:** `nexus-exchange-cli` (`nexus`) is a thin command layer over the Rust SDK.
* **CCXT:** the API exposes CCXT-compatible endpoints (tagged in the OpenAPI spec), so existing CCXT-based MM tooling connects with minimal changes.

## 7. Testnet status

This is a testnet preview: balances are testnet USDX (no real funds), state can reset on redeploy, and tier/rate-limit ceilings are configurable and may change as the tier system is finalized across release gates. Treat API keys as re-creatable.

***

*Doc note (ENG-2755): the tier ceilings above track* [*`rate-limits.md`*](/exchange/apis-and-rates/rate-limits)*. One known inconsistency to reconcile (ENG-4062): that page lists the Pro tier at 200 req/s while the indexer's in-code default (`DEFAULT_PRO_RPS`) is 20 — the gateway config is the effective authority for the public number; the discrepancy is flagged for reconciliation, not resolved here.*


# API Versioning

The Nexus Exchange API is versioned with the OpenAPI specification published at [`nexus-xyz/nexus-exchange-api`](https://github.com/nexus-xyz/nexus-exchange-api). Each SDK release is pinned to a specific spec version, and the API declares a **minimum supported version** that it will accept. This page explains how to send your version, what happens when it falls below the minimum, and how deprecations are signalled.

## Sending your version

Send the spec tag your client was built against in a request header, in addition to (not instead of) your `User-Agent`:

```
X-Nexus-Api-Version: v0.6.2
```

The value is the released spec tag (a leading `v` is optional — `v0.6.2` and `0.6.2` are equivalent). The official SDKs are pinned to a spec version and send this header for you by default; you only need to set it manually if you build your own client.

The header is **optional during the current pre-1.0 grace period** — requests without it are accepted. This will tighten as the API approaches GA; send the header now so your integration is forward-compatible.

## Minimum supported version

To avoid stranding integrations on breaking changes — while not carrying permanent backward-compatibility baggage before there is a large external client base — the API accepts requests only at or above a published minimum version.

**Pre-1.0 support policy.** Until the 1.0 GA release, the minimum supported version may advance aggressively as breaking changes ship. Breaking changes are a minor bump (`0.X.0`); non-breaking changes are a patch bump. The operational practice when the minimum advances is to first move versions below it into a deprecation window (see below) before retiring them, so an active integration gets a signalled window to upgrade. Treat the `Deprecation` / `Sunset` response headers as your authoritative upgrade signal rather than assuming a fixed grace period.

You can always read the current threshold programmatically — see [Discovering the current version](#discovering-the-current-version).

## Below the minimum: `426 Upgrade Required`

A request whose version is below the minimum is rejected with **HTTP 426 Upgrade Required** and a machine-readable JSON body:

```json
{
  "code": "api_version_unsupported",
  "message": "Unsupported or missing API version. Fetch the current OpenAPI spec at spec_url, regenerate your client, and retry with the X-Nexus-Api-Version header.",
  "min_version": "0.6.0",
  "current_version": "0.6.2",
  "spec_url": "https://github.com/nexus-xyz/nexus-exchange-api/releases",
  "docs_url": "https://docs.nexus.xyz/exchange/apis-and-rates/api-versioning"
}
```

The response also carries an `X-Nexus-Api-Min-Version` header with the same floor, for clients that read headers without parsing the body.

**Self-healing for agents.** Because the body carries `spec_url` and both the minimum and current versions, an automated client can recover from version skew on its own: fetch the current spec, regenerate its client, and retry — no human in the loop. A version-skew error is a recoverable condition, not silent breakage.

## Deprecation and sunset

A version that is still accepted but scheduled for retirement is served normally, with standard advisory headers on every response:

* **`Deprecation`** ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745)) — signals that the version you sent is deprecated.
* **`Sunset`** ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594)) — the date after which that version will be rejected with `426`.
* **`Link: <…>; rel="deprecation"`** — points to this page.

Treat a `Deprecation` header as a prompt to upgrade before the `Sunset` date. Nothing about the request fails while it is in the deprecation window; only the advisory headers are added.

## Header parsing details

A few specifics worth knowing if you build your own client rather than using an official SDK:

* **Pre-release tags compare by release lineage.** A pre-release such as `0.6.0-rc.1` is treated as `0.6.0` for the minimum check — everything from the first `-` or `+` is stripped. So a pre-release satisfies a floor equal to its release version.
* **An unparseable version is treated as missing.** During the grace period a malformed `X-Nexus-Api-Version` is accepted and logged, exactly like an absent header; once the header becomes required it is rejected the same way. Send a clean `major.minor.patch` tag (an optional leading `v`).
* **CORS preflight and WebSocket handshakes are never gated.** `OPTIONS` preflight and WebSocket upgrade handshakes are exempt — a browser cannot attach the header to either — so the gate never blocks them.
* **Discovery routes are never gated.** `/metadata`, `/openapi.json`, `/llms.txt`, and the `/.well-known/*` documents stay reachable at any version, so a stale client can always fetch what it needs to self-heal.

## Discovering the current version

The current minimum and latest versions are published at an unauthenticated metadata endpoint, so clients and agents can read the threshold without scraping docs:

```
GET /metadata
```

```json
{
  "api_version": {
    "header": "x-nexus-api-version",
    "min_supported": "0.6.0",
    "current": "0.6.2",
    "deprecated_below": null,
    "sunset": null,
    "spec_url": "https://github.com/nexus-xyz/nexus-exchange-api/releases",
    "docs_url": "https://docs.nexus.xyz/exchange/apis-and-rates/api-versioning",
    "policy": "pre-1.0: minimum-supported version may advance until GA; missing version header allowed during grace mode"
  }
}
```

`/metadata` is always reachable regardless of the version you send — otherwise an out-of-date client could never learn the value it needs to upgrade.

## Recommended client pattern

1. Pin your client to a released spec version and send it in `X-Nexus-Api-Version`.
2. On a `426 api_version_unsupported`, read `spec_url` from the body (or `GET /metadata`), regenerate against a version `>= min_supported`, and retry.
3. Watch for `Deprecation` / `Sunset` response headers and upgrade before the sunset date.


# Exchange Testnet

The Nexus Exchange is live on testnet at [exchange.nexus.xyz](https://exchange.nexus.xyz).

You can authenticate via the API, place orders, and trade across the live perpetual futures markets.

The frontend displays live exchange state in read-only mode. All trading is conducted via the REST and WebSocket APIs.

Full API documentation is hosted at [exchange.nexus.xyz/api-docs](https://exchange.nexus.xyz/api-docs).

### Getting Started

#### 1. Authenticate

Sign in with your Ethereum wallet using an EIP-191 personal signature:

```bash
POST /auth/login
```

Sign the message `"Sign in to Nexus Exchange"` to receive a session token (24-hour TTL). Your account is created automatically on first sign-in.

#### 2. Create an API Key

```bash
POST /keys
```

Returns an HMAC key pair (`key_id` + `secret`). The secret is shown once — store it immediately.

Manage keys with `GET /keys` (list) and `DELETE /keys/{key_id}` (revoke).

#### 3. Fund your account

Credit your testnet account with synthetic USDX from the faucet:

```bash
POST /account/credit
```

You need a USDX balance before you can place orders. Check your balance at any time via `GET /account`.

**Each API key can claim up to 500 test USDX per day.**

#### 4. Place an Order

Authenticate all trading requests with three headers: `X-API-Key`, `X-Timestamp`, `X-Signature`.

```bash
POST /orders
```

```json
{
  "market_id": "BTC-USDX-PERP",
  "side": "Buy",
  "order_type": "Limit",
  "price": "67000",
  "quantity": "0.01",
  "time_in_force": "GTC"
}
```

See the [API documentation](https://exchange.nexus.xyz/api-docs) for complete authentication examples and all available endpoints.

### Markets

Four markets are live on testnet today — BTC, ETH, SOL, and NDQ. The configured set expands to 32 perpetual futures pairs across crypto, FX, commodities, and indices, all denominated in USDX.

| Category     | Assets                                                                                                            |
| ------------ | ----------------------------------------------------------------------------------------------------------------- |
| Major Crypto | BTC, ETH, SOL                                                                                                     |
| Altcoins     | AVAX, DOT, ADA, ATOM, NEAR, SUI, APT, TIA, SEI, INJ, LINK, UNI, AAVE, MKR, SNX, CRV, FIL, WIF, DOGE, ARB, OP, POL |
| FX           | EUR, GBP, JPY                                                                                                     |
| Commodities  | GOLD, OIL                                                                                                         |
| Indices      | SPX, NDQ                                                                                                          |

All markets are denominated in USDX. Query all available markets via `GET /markets/summary`.

### Analytics Dashboard

Real-time exchange telemetry exists at [exchange.nexus.xyz/analytics](https://exchange.nexus.xyz/analytics):

* **Engine** — fills/second, throughput, HTTP latency, resource utilization
* **Microstructure** — cross-market spread, depth, imbalance, order book heatmap
* **Risk** — account risk distribution, insurance fund flow, liquidation monitoring
* **Oracle** — mark price divergence per market, feed status
* **Invariants** — continuous correctness monitoring (fund conservation, OI symmetry)


# USDX

**USDX is the Nexus Exchange's native margin and quote currency, pegged 1:1 to the US dollar.** Every market on the Exchange is denominated and margined in USDX, so traders hold a single collateral asset across spot and perpetual futures.

## What makes USDX different

* **Yield on idle capital.** USDX earns yield from US Treasury bills on balances that aren't actively deployed — not from new token issuance or inflationary rewards.
* **An exchange primitive.** USDX exists to make the Exchange work well for traders: one unified collateral and quote asset, composable with the EVM ecosystem on the Nexus blockchain.
* **Backed and pegged 1:1 to USD.**

## What USDX is not

USDX is not a standalone DeFi stablecoin protocol or a yield product. It is the collateral and quote layer of the Exchange.

## Learn more

USDX backs every position on the Exchange — see [Margining](/exchange/trading/perpetuals/margining) for how collateral and leverage work, and [Quickstart](/exchange/trading/quickstart) to fund an account and place your first order.


# Overview

Programmatic access to the Nexus Exchange — OpenAPI spec, SDKs, CLI, and MCP.

The Nexus Exchange is API-first: everything a trader can do is available programmatically. The **Interfaces** are the officially supported clients for that API — a machine-readable OpenAPI specification, language SDKs, a command-line tool, and an MCP server for AI agents. All of them speak to the same REST and WebSocket gateway documented under [APIs & Rates](/exchange/apis-and-rates); pick whichever fits how you build.

### The OpenAPI specification

The API is contract-first. The machine-readable schema — every route, request/response body, and CCXT method mapping — is published at [`nexus-xyz/nexus-exchange-api`](https://github.com/nexus-xyz/nexus-exchange-api) and served live at `/openapi.json`. Releases are tracked on [GitHub Releases](https://github.com/nexus-xyz/nexus-exchange-api/releases). The SDKs, CLI, and MCP server below are all generated from — or pinned to — a released version of this spec, so they stay in lockstep with the gateway.

### SDKs

| Language   | Repository                                                            | Use it for                                       |
| ---------- | --------------------------------------------------------------------- | ------------------------------------------------ |
| Rust       | [`nexus-exchange-rs`](https://github.com/nexus-xyz/nexus-exchange-rs) | Latency-sensitive clients and market-making bots |
| TypeScript | [`nexus-exchange-ts`](https://github.com/nexus-xyz/nexus-exchange-ts) | Web, Node, and edge applications                 |
| Python     | [`nexus-exchange-py`](https://github.com/nexus-xyz/nexus-exchange-py) | Research, backtesting, and scripting             |

Each SDK handles request signing (HMAC), pagination, and the WebSocket subscription lifecycle so you don't reimplement them. Version support and breaking-change policy are documented in each repository's README.

For the account and portfolio surface — consolidated account state, the withdrawable balance, the fee schedule, enriched position risk fields, and the equity/PnL/volume time-series — see [Portfolio & Account State](/interfaces/portfolio), which covers all four interfaces together.

### Command line

The [`nexus-exchange-cli`](https://github.com/nexus-xyz/nexus-exchange-cli) wraps the same API for interactive use and shell scripting — manage keys, place and cancel orders, and query account state without writing code. `nexus --version` reports the API spec and SDK versions it is built against.

### MCP server

The [`nexus-exchange-mcp`](https://github.com/nexus-xyz/nexus-exchange-mcp) server exposes the Exchange as [Model Context Protocol](https://modelcontextprotocol.io) tools, so an AI assistant or agent can trade and read account state through the same authenticated API a human client uses.

### Choosing a network

The Exchange runs on **Nexus Testnet** today as a development preview, with a public **mainnet** to follow. Every interface targets one network at a time, selected by the gateway base URL it is pointed at — see [APIs & Rates](/exchange/apis-and-rates) for the current base URL and the [Quickstart](/exchange/trading/quickstart) for the end-to-end connection flow. Credentials are scoped to the network they are created on: a testnet API key is not valid against mainnet, and vice versa.

### Authentication

Authentication is identical across every interface. You sign a fixed message with your wallet (EIP-191) to obtain a short-lived session token, use that token once to mint an HMAC API key, then sign each trading request with that key. The SDKs and CLI perform the request signing for you. The full walkthrough, with runnable examples, is in the [Quickstart](/exchange/trading/quickstart).

> **Status:** development preview on testnet. The interfaces track the OpenAPI spec release-by-release; pin to a spec version in production and consult each repository's release notes before upgrading. Testnet credentials and balances have no real-world value.


# Portfolio & Account State

Consolidated account state, withdrawable balance, fee schedule, enriched positions, and the portfolio time-series — across the SDKs and CLI.

The portfolio surface answers four questions about an account in one place: what is it worth right now, what can actually be withdrawn, what does it get charged, and how has it performed over time. It is exposed as a small set of authenticated REST endpoints plus enriched per-position risk fields, and every interface — the Rust, TypeScript and Python SDKs and the CLI — reaches the same routes on the gateway documented under [APIs & Rates](/exchange/apis-and-rates).

If you are building a portfolio view, read [Reading these values safely](#reading-these-values-safely) before you render anything. Several fields are deliberately nullable, one sign convention is easy to invert, and there is one race condition that a naive two-call implementation will hit in production.

### Getting it

```bash
npm install @nexus-xyz/exchange-ts    # TypeScript
cargo add nexus-exchange              # Rust

# Python is not on PyPI yet — install from source:
pip install git+https://github.com/nexus-xyz/nexus-exchange-py
```

The CLI is distributed through [`nexus-exchange-cli`](https://github.com/nexus-xyz/nexus-exchange-cli) releases. See the [Interfaces overview](/interfaces/interfaces) for all four clients.

### Authentication

Every route on this page is account-scoped and requires an HMAC API key — there is no public or unauthenticated variant. Requests carry `X-API-Key`, `X-Timestamp` and `X-Signature`, and the timestamp must be within 30 seconds of server time. The SDKs and CLI sign for you. The full flow, with runnable cURL, is in the [Quickstart](/exchange/trading/quickstart).

Keep the API secret out of source and out of shell history — it is shown once at creation and cannot be retrieved later. The examples below read it from the environment.

### Choosing a network

The Exchange runs on Nexus Testnet today; mainnet will follow. Select the network by pointing the interface at the matching gateway base URL (see [APIs & Rates](/exchange/apis-and-rates)). Credentials are scoped to the network they were created on, so a testnet key will not authenticate against mainnet.

### Reference

| Method | Path                                        | Auth | Description                                                            |
| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------------------- |
| GET    | `/account/state`                            | HMAC | Portfolio summary **and** every open position, from one coherent read  |
| GET    | `/account/summary`                          | HMAC | Portfolio summary alone — the same object embedded in `/account/state` |
| GET    | `/account/fees`                             | HMAC | Effective fee schedule for the account                                 |
| GET    | `/account/portfolio-history?window=&limit=` | HMAC | Equity, cumulative PnL and cumulative volume time-series               |

Per-SDK entry points:

| Interface  | Consolidated state      | Fee schedule           | Time-series                                        |
| ---------- | ----------------------- | ---------------------- | -------------------------------------------------- |
| Rust       | `fetch_account_state()` | `fetch_account_fees()` | `fetch_portfolio_history(window, limit)`           |
| Python     | `fetch_account_state()` | `fetch_account_fees()` | `fetch_portfolio_history(window, limit)`           |
| TypeScript | `getAccountState()`     | `getAccountFees()`     | `getPortfolioHistory({ window, limit })`           |
| CLI        | `nexus account state`   | `nexus account fees`   | `nexus account portfolio-history --window --limit` |

### Consolidated account state

`GET /account/state` returns the portfolio aggregates and all open positions built from **one** server-side read. Because both halves come from the same snapshot, `summary.open_positions_count` always equals the length of `positions`.

The summary carries `collateral`, `total_equity`, `total_unrealized_pnl`, `total_realized_pnl_24h`, `total_volume_24h`, `open_positions_count`, `open_orders_count`, `margin_used`, `available_margin` and `withdrawable`.

**`withdrawable`** is the balance that can actually leave the account: engine-authoritative free margin floored at zero, `max(0, available_margin)`. Free margin already nets each position's initial margin and every pre-trade order reservation out of equity, so this is the amount a withdrawal can draw on — not `total_equity`, and not `collateral`. An underwater account clamps to `"0"` and is never reported negative. It is derived from the authoritative margin view, so when that view is unavailable the endpoint **fails closed with `502`** rather than returning a locally estimated number.

### Fee schedule

`GET /account/fees` reports what the venue charges the account today — the forward-looking schedule rate, not a realized average over past fills.

| Field                  | Notes                                                                                |
| ---------------------- | ------------------------------------------------------------------------------------ |
| `maker_fee_bps`        | Basis points. **May be negative**, which means the maker is paid a rebate            |
| `taker_fee_bps`        | Basis points — `5` is 0.05%                                                          |
| `tier`                 | Currently always `base`; there are no per-account tiers yet                          |
| `schedule`             | Scope of the reported rate. Currently always `standard`                              |
| `volume_30d`           | Rolling 30-day traded notional, decimal string. Best-effort — see the flag below     |
| `volume_30d_estimated` | `true` when `volume_30d` may **undercount** (the source fill buffer was at capacity) |
| `discounts`            | Active discounts. Currently always empty                                             |

`tier`, `schedule` and `discounts` are provisional: the fee model is still being finalized, and `discounts` entries have no guaranteed properties yet. Treat the two `_bps` integers as the stable part of this response and do not branch on a discount shape that does not exist.

### Portfolio time-series

`GET /account/portfolio-history` returns equity, cumulative trading PnL and cumulative traded notional over a window, downsampled server-side. Points are **oldest first**.

| `window` | Cadence | Max points | Span  |
| -------- | ------- | ---------- | ----- |
| `day`    | 5 min   | 288        | 24 h  |
| `week`   | 1 h     | 168        | 7 d   |
| `month`  | 6 h     | 120        | 30 d  |
| `all`    | 1 d     | 366        | \~1 y |

Omitting `window` gives `day`. A value outside that set is rejected with `400` (`invalid_window`); if the parameter is repeated, the first value is used. `limit` must be between `1` and `366` — inside that range it narrows the result and is **clamped** to the window's capacity rather than rejected, so asking for 366 points of `day` returns 288 rather than an error; outside it, the request is rejected with `400`.

The response echoes the `window` and the `cadence_ms` that were actually served. Read them back rather than assuming the values you sent: it is the difference between labelling an axis correctly and labelling it plausibly.

Each point carries `timestamp_ms`, `equity`, `pnl` and `volume`. `pnl` and `volume` are **cumulative up to that sample**, not per-interval — to chart per-interval activity, difference adjacent points yourself.

### Enriched position fields

Positions returned by `/account/state` (and `/positions`) carry per-position risk detail alongside `market_id`, `side`, `size`, `entry_price`, `unrealized_pnl` and `realized_pnl`:

| Field            | Meaning                                                                |
| ---------------- | ---------------------------------------------------------------------- |
| `notional_value` | `abs(size) × mark price`                                               |
| `margin_used`    | Initial margin held against the position, under the cross-margin model |
| `roe`            | Return on initial margin: `unrealized_pnl / margin_used`               |
| `max_leverage`   | Maximum leverage the market allows, from its risk params               |
| `leverage`       | The account's leverage multiplier for this position                    |
| `funding_paid`   | Cumulative funding on the position — see the sign convention below     |

These are computed on the low-latency read path rather than by round-tripping to the matching engine, which is what keeps the endpoint fast. The trade-off is that when one of their inputs is not available on that path, the field is `null` and a companion `<field>_error` carries a machine-readable reason instead of a fabricated number.

**`leverage` is currently always `null`,** with `leverage_error` set to `margin_state_not_mirrored`: deriving it needs the account's leverage setting or its allocated margin, and neither is available on the read path. Do not reconstruct it from `margin_used` — that expression collapses to `1 / initial_margin_rate`, which is a per-market constant, not the position's real leverage. Showing that as leverage would be confidently wrong on every position in the market.

**`funding_paid` is paid-positive.** A positive value means the position has *paid* funding; a negative value means it has *received* funding. It is always present, `"0"` before any funding accrues, and bounded by the funding history the venue retains. Inverting this sign turns a cost into income on a P\&L screen, so it is worth a test.

### Reading these values safely

**Monetary values are decimal strings, not numbers.** `equity`, `pnl`, `volume`, `withdrawable`, `notional_value` and the rest are arbitrary-precision decimals serialized as strings so they are lossless. Parse them with a decimal type. Passing them through a float — `parseFloat`, `float()`, `as f64` — reintroduces exactly the rounding error the string encoding exists to prevent. Leverage fields (`leverage`, `max_leverage`) are genuine JSON numbers.

**Derived fields have three states, not two.** Each of the five computed position fields — `notional_value`, `margin_used`, `roe`, `leverage` and `max_leverage` — can be:

1. **a value** — computed and authoritative;
2. **`null`** — reported, but not computable; the paired `<field>_error` says why;
3. **absent** — the server predates the field.

Collapsing any of these into `0` invents data. "Not reported", "not computable" and "zero" are three different answers to a user asking what their position is worth, and only one of them is a number. Render the missing cases as an explicit gap — the CLI prints `-` — and surface the `<field>_error` when you have it. The same applies to `withdrawable`: it is optional in the schema, so an older deployment can omit it, and defaulting that to `"0"` would tell someone they have nothing available when the truth is that nobody asked.

Those five are exactly the fields carrying a companion `<field>_error`. `funding_paid` is not one of them — it is always present, so there is no `funding_paid_error` to branch on.

**Prefer `/account/state` over two calls.** Fetching `/account/summary` and `/positions` separately is two independent requests against a live account. A fill landing between them returns an aggregate that disagrees with the position list — `open_positions_count` says three, the array has four — and the window is real under any load. The consolidated endpoint exists so that one coherent read backs both halves.

**Handle the failure codes distinctly.** `401` is a credential or clock problem — check that `X-Timestamp` is within 30 seconds of server time before assuming the key is wrong. `429` means you exceeded the rate budget; see [Rate & Connection Limits](/exchange/apis-and-rates/rate-limits) and back off rather than retrying immediately. `502` on `/account/state` and `/account/summary` means the authoritative margin view was unreachable and the server declined to guess — retry, and do not fall back to a locally computed `withdrawable`.

### Example

```bash
export NEXUS_API_KEY=nx_7f3a1b...          # the key ID is not secret

# Prompt for the secret instead of typing it inline — an `export
# NEXUS_API_SECRET=...` would leave it in your shell history.
read -rs NEXUS_API_SECRET && export NEXUS_API_SECRET

nexus account state
nexus account fees
nexus account portfolio-history --window week
```

```typescript
import { Client, Network } from "@nexus-xyz/exchange-ts";

const client = new Client({
  network: Network.Stable,
  apiKey: process.env.NEXUS_API_KEY!,
  apiSecret: process.env.NEXUS_API_SECRET!,
});

// One coherent read: open_positions_count cannot disagree with positions.length.
const { summary, positions } = await client.getAccountState();

// Absent is not zero — say so rather than defaulting.
console.log(`withdrawable: ${summary.withdrawable ?? "<not reported>"}`);

for (const p of positions) {
  // null carries a reason in the companion field; absent carries nothing.
  const roe = p.roe ?? (p.roe_error ? `<${p.roe_error}>` : "<not reported>");
  // Paid-positive: a positive funding_paid means this position paid.
  console.log(`${p.market_id} ${p.side} ${p.size}  roe=${roe}  funding_paid=${p.funding_paid}`);
}

// The response echoes what was served — read it back, don't assume.
const history = await client.getPortfolioHistory({ window: "week" });
console.log(`${history.window} @ ${history.cadence_ms}ms, ${history.points.length} points`);
```

```json
{
  "summary": {
    "collateral": "25000.00",
    "total_equity": "25500.00",
    "total_unrealized_pnl": "500.00",
    "margin_used": "1075.00",
    "available_margin": "24425.00",
    "withdrawable": "24425.00",
    "open_positions_count": 1,
    "open_orders_count": 0
  },
  "positions": [
    {
      "market_id": "BTC-USDX-PERP",
      "side": "Long",
      "size": "0.25",
      "entry_price": "84000.00",
      "unrealized_pnl": "500.00",
      "notional_value": "21500.00",
      "margin_used": "1075.00",
      "roe": "0.4651",
      "max_leverage": 20,
      "leverage": null,
      "leverage_error": "margin_state_not_mirrored",
      "funding_paid": "3.21"
    }
  ]
}
```

The numbers above are self-consistent, which is worth tracing once: at a mark price of `86000`, `notional_value` is `0.25 × 86000`, `unrealized_pnl` is `0.25 × (86000 − 84000)`, `margin_used` is `notional_value × 1/max_leverage`, `roe` is `unrealized_pnl / margin_used`, and `withdrawable` equals `total_equity − margin_used` because there are no open orders reserving margin.

Runnable end-to-end examples live in the SDK repositories: [`examples/portfolio.ts`](https://github.com/nexus-xyz/nexus-exchange-ts/blob/main/examples/portfolio.ts) and [`examples/portfolio.rs`](https://github.com/nexus-xyz/nexus-exchange-rs/blob/main/examples/portfolio.rs).

### Related

* [APIs & Rates](/exchange/apis-and-rates)
* [Exchange REST](/exchange/apis-and-rates/exchange-rest)
* [Rate & Connection Limits](/exchange/apis-and-rates/rate-limits)
* [Interfaces overview](/interfaces/interfaces)
* [Quickstart](/exchange/trading/quickstart)

> **Status:** development preview on testnet. These routes ship in OpenAPI spec v0.7.2; pin to a spec version in production and check each SDK's release notes before upgrading. `tier`, `schedule` and `discounts` on `/account/fees` are provisional and finalize with the fee model, and `leverage` reports `null` until the margin state it needs is mirrored. Testnet credentials and balances have no real-world value.


# Overview

The Math Engine derives the Exchange's mathematics directly from its own implementation, rather than describing it by hand: every expression on these pages is extracted from the Exchange's source code, adversarially checked against it and its own test suite, and versioned with a changelog whenever the underlying mathematics genuinely changes.

## Pages

* [`engine.md`](/math-engine/engine) — the engine itself, formalized: how expressions are derived and verified, what each stage guarantees, and what is not yet verified.
* [`global.md`](/math-engine/global) — the global state-space model composed across all systems.
* [`system.md`](/math-engine/system) — the cross-model relationship graph: how expressions in one system feed, bound, or trigger expressions in another.
* [`closed-loop.md`](/math-engine/closed-loop) — the oracle-endogenous closed loop.
* [`funding-rate.md`](/math-engine/funding-rate), [`margin-math.md`](/math-engine/margin-math), [`liquidation-engine.md`](/math-engine/liquidation-engine), [`oracle.md`](/math-engine/oracle), [`order-book.md`](/math-engine/order-book), [`position-tracker.md`](/math-engine/position-tracker), [`insurance-fund.md`](/math-engine/insurance-fund), [`settlement.md`](/math-engine/settlement) — per-system derived mathematics: expressions, invariants, and citations into the source.
* [`performance.md`](/math-engine/performance) — analytical performance/scaling model fitted to benchmark data.

## How this is produced

Each expression is derived from the Exchange's implementation, then checked by an independent adversarial pass whose only goal is to try to disprove it against the code and the code's own tests. See [`engine.md`](/math-engine/engine) for the full trust model and its known limits.


# The Engine, Formally

The corpus you are reading is produced by a machine. This page documents and formalizes that machine: its AI actors, its pipeline, what each stage guarantees, and — just as important — what is *not* yet verified.

## The engine as a composition

Let $$\Sigma$$ be the Exchange's source code (the ground truth) and $$\mathcal{M}$$ the space of structured mathematical models. The corpus is the image of the source under a composition of maps:

$$
\mathcal{C} ;=; R \circ P \circ J \circ G,(\Sigma)
$$

* $$G$$ **— the Generator** (LLM): per system, $$G\_k : \Sigma\_k \to M\_k$$ reads the Rust source and derives a structured model — expressions with LaTeX and an *executable* Python form, typed domains and units, test vectors extracted from the code's own tests, chart and figure specifications. $$G$$ runs under two locks that stabilize re-derivation: the corpus-wide **notation lock** and the **prior-model baseline**.
* $$J$$ **— the Judge** (LLM, adversarial): $$J\_k : (M\_k, \Sigma\_k) \to$$ verdicts. Its only goal is to *refute* the Generator — formula vs. cited code, rounding semantics, edge regimes — and to extract further test vectors. Vectors merge only if they pass numeric evaluation; a refutation trumps passing vectors. The Judge has caught two real derivation bugs to date (the partial-liquidation full-close branch; the settlement merge summation).
* $$P$$ **— the composition passes** (LLM): the relationship graph $$P\_{\text{sys}} : {M\_k} \to \mathcal{G}$$, the global state-space model $$P\_{\text{glob}} : ({M\_k}, \mathcal{G}) \to M\_\ast$$ (rendered as the Exchange Engine and the closed loop), the **state-space classifier** $$P\_{\text{ss}} : {M\_k} \to \mathcal{S}$$ (v0.0.9: every corpus quantity classified state / input / parameter / derived; the full hierarchical state vector; the completed event alphabet — rendered as the formal appendix, its totality enforced mechanically), and the specification passes for surfaces, structural figures, and the analytical performance model. These run strictly *after* $$G$$ and $$J$$ — the composition reads verified inputs.
* $$R$$ **— the renderers** (mechanical, no LLM): deterministic Markdown with numbered equations and resolved citations, 1-D response curves, sensitivity elasticities, 2-D surfaces and regime maps, lane diagrams, performance fits. Models are the single source of truth; $$R$$ never adds content.

Gates sit between the stages: source-hash **staleness**, numeric **test vectors** (every expression must carry passing vectors), **dimensional analysis** (units balance, ast-checked), the **notation** and **coherence** gates (one symbol → one quantity → one dimension; duplicated quantities agree numerically), and since v0.0.9 the **closure gate** — the completeness of the formal appendix as a checked property: no orphan expressions, every state coordinate written by an event and read somewhere, every event map citing only defined coordinates. A corpus release is an immutable snapshot of $$\mathcal{C}$$ with a semantic changelog. The residue that survives every adversarial pass without being attributable to a modeling error exits the pipeline as a **design finding** — the engine's second product, alongside the corpus itself.

## What each stage guarantees — and what it does not

| Layer                          | Verified by                                                                 | Status                  |
| ------------------------------ | --------------------------------------------------------------------------- | ----------------------- |
| Expression ↔ code              | Judge (adversarial) + vectors + dims                                        | strong                  |
| Re-derivation stability        | notation lock + baseline + semantic differ                                  | strong                  |
| Cross-model coherence          | coherence gate (numeric agreement of duplicated quantities) + notation gate | strong                  |
| Graph / global / closed loop   | composition Judge (adversarial) + reconciliation audit                      | strong                  |
| System-wide invariants         | property-tested (simulator, 512 trials each) or Judge-ruled if structural   | strong\*                |
| Completeness of the definition | closure gate over the state-space classification (mechanical)               | strong — v0.0.9         |
| Design quality (vs. peers)     | not evaluated                                                               | v0.0.10 (Market Critic) |

\*with an honest caveat: the composition Judge *refuted* both closed-loop invariants as originally stated, and the corpus now states them **conditionally** — the equity-shock bound covers the anchor leg on non-re-anchor prints, and staleness fail-closed holds only with the trade-reference leg frozen and settlement suspended, assumptions no component expression yet witnesses. The unresolved residue was not hidden inside a weaker claim: it became the first entries of the findings log, the engine's channel for design-level observations that belong to engineering rather than to the corpus. Since v0.0.8 the composition layer is no longer trusting: components still derive in parallel, but a coherence gate checks their duplicated quantities numerically, a composition Judge attacks the graph and global model, an invariant simulator property-tests every numeric claim, and a final reconciliation audit closes each cycle — its directives (and any new design findings) feed the next one.

## Actor configuration

All actors run headless against a single pinned large language model and a fixed, versioned prompt per actor, and every run is recorded with its model, duration, token usage, and cost.

* **Model**: every run to date has executed on the same pinned model. Effort has been pinned at the highest tier since v0.0.8.
* **Prompts**: each actor's full prompt is versioned with the engine, so every corpus release is attributable to an exact prompt + model pair via the engine's own history.

## The trust loop, end to end

$$
\Sigma \xrightarrow{;G;} M \xrightarrow{;J;} M^{\checkmark}
\xrightarrow{;P;} (\mathcal{G}, M\_\ast)
\xrightarrow{;R;} \text{docs, charts, figures}
\xrightarrow{;\text{gates};} \text{release } v\_n
$$

A source change breaks the staleness gate → re-derivation under the locks → the Judge re-verifies → the semantic differ writes the changelog: only real mathematical change is flagged; renames and refactors are recognized as equivalent. The reader-facing promise is that every equation on this site is (1) pinned to the exact source it describes, (2) numerically tested against that source's own test suite plus adversarially extracted vectors, and (3) dimensionally consistent — with the composition layers explicitly labeled as the current frontier of verification.

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.


# Global Model

Fix a family of markets $$k$$, accounts $$a$$, and per-account-per-market positions. The engine is the transition system $$S' = f\_e(S, u)$$ where $$e$$ ranges over a finite alphabet of engine events — deposit and withdrawal, order submission with matching, cancel/expiry, funding accrual, funding settlement, and the liquidation cascade — and $$u = (P\_{oracle,k}, t\_{last,k})*k$$ is an exogenous input: a trusted price and its timestamp per market, handed to the engine as given. From $$u$$ and its own trade window $$W\_k$$ the engine derives the mark $$m\_k = w,P*{oracle,k} + (1-w),P\_{trade}(W\_k)$$ ([(O.8)](/math-engine/oracle), [(O.7)](/math-engine/oracle)); every event map below is a function of $$(S, u)$$ through $$m\_k$$ alone. The orbit of the Exchange is the trajectory of $$S$$ under an interleaving of these maps: fills build entry prices ([(T.1)](/math-engine/position-tracker)), marks revalue them ([(T.2)](/math-engine/position-tracker)), funding transfers value between the sides ([(F.4)](/math-engine/funding-rate)), and the cascade repossesses positions whose equity has fallen to the maintenance floor ([(L.4)](/math-engine/liquidation-engine)).

The coupling quantity that organizes the whole engine is **equity**, $$E = C + \Pi$$ ([(M.5)](/math-engine/margin-math)): affine in the mark, additive over positions, and read by every guard that matters. Admission compares equity headroom against initial margin ([(M.13)](/math-engine/margin-math), [(M.14)](/math-engine/margin-math)); the liquidation trigger compares it against maintenance margin ([(L.4)](/math-engine/liquidation-engine)); withdrawal deliberately ignores it and moves only realized collateral ([(M.16)](/math-engine/margin-math)). Because maintenance is strictly inside initial ($$r\_m < r\_i$$, [(M.4)](/math-engine/margin-math) vs [(M.2)](/math-engine/margin-math)), the state space is stratified into a healthy region, a buffer, and the liquidation region — and the engine's dynamics are precisely the story of how event maps move accounts between these strata while conserving value at every step.

## The state space

**The state set.** A state is the tuple

$$S = \big( (C\_a, M\_{resv,a}, n\_{resv,a})*a,\ (s*{a,k}, P\_{e,a,k})*{a,k},\ (B\_k, A\_k, T\_k, W\_k, \Phi\_k, \Sigma*{\mathrm{abs},k}, \Sigma\_{\mathrm{rec},k})*k,\ C*{pool},\ C\_{fee} \big)$$

with components, per account, realized collateral, reserved margin, and the open-reservation count; per position, a signed size $$s$$ (direction $$\sigma = \operatorname{sign}(s)$$, magnitude $$q = |s|$$) and a volume-weighted entry price $$P\_e$$; per market, the resting-order multiset $$B\_k$$ (each order carrying side, limit price, and remaining quantity $$q - q\_f$$), the funding accumulator pair $$(A\_k, T\_k)$$, the five-trade window $$W\_k$$, and the insurance fund balance $$\Phi\_k$$ with its lifetime ledgers; and two system accounts, the funding pool $$C\_{pool}$$ and the Exchange fee account $$C\_{fee}$$. The input $$u\_k = (P\_{oracle,k}, t\_{last,k})$$ and its guard bookkeeping $$\Omega\_k$$ (ten-print history, pending re-anchor block) are input-process state, exogenous to the engine but declared here because Part II's map writes them.

**The per-position funding accumulator is not a coordinate.** The corpus fixes the convention $$\varphi\_{a,k} \equiv 0$$: funding accrual touches only the market pair $$(A\_k, T\_k)$$ ([(F.2)](/math-engine/funding-rate)), and settlement computes each payment fresh from $$(A, T)$$ and resets atomically — no event map ever writes a nonzero value into a per-position accumulator, so carrying one would be writer-less state, not well-defined dynamics. Consequently the funding-integral term of [(M.8)](/math-engine/margin-math) reads identically zero at every observable state, and live equity is $$E\_{pf} = C + \sum\_i s\_i (m\_i - P\_{e,i})$$: the funding channel into equity is the collateral debit at settlement, nothing else.

**The admissible region** $$\mathcal{A}$$ is cut out by: strictly positive stored position sizes $$q\_{a,k} > 0$$ with $$|s|$$ on the lot lattice $$\ell\mathbb{Z}$$ ([(B.1)](/math-engine/order-book)); positive entry prices; resting limit prices strictly positive on the tick lattice $$\delta\mathbb{Z}$$ ([(B.2)](/math-engine/order-book)) with the book uncrossed, $$P\_b < P\_a$$; strictly positive remaining quantity on every resting order; $$M\_{resv} \ge 0$$ and $$n\_{resv} \ge 0$$; market parameters with $$0 \le r\_m < r\_i$$ (validated at parse, the ordering behind [(M.4)](/math-engine/margin-math) $$\le$$ [(M.2)](/math-engine/margin-math)); $$\Phi\_k \ge 0$$ with $$\Phi = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}}$$; $$T\_k \ge 0$$; and $$C\_a$$ *unrestricted in sign* — collateral can be driven negative by unguarded engine-internal debits, and the model keeps that honesty rather than assuming $$C\_a \ge 0$$.

**The event alphabet and totality.** The engine and input alphabets are

$$\begin{gathered} \Sigma\_E = {\texttt{deposit\_withdrawal},\ \texttt{order\_submission\_fill},\ \texttt{order\_cancel\_expiry}, \ \texttt{funding\_accrual},\ \texttt{funding\_settlement},\ \texttt{liquidation\_cascade}}, \qquad \Sigma\_O = {\texttt{oracle\_print}}. \end{gathered}$$

Every map is well-defined on $$\mathcal{A}$$ because each carries explicit domain guards: withdrawal is gated by the flat-and-unreserved predicate ([(M.16)](/math-engine/margin-math)); admission by alignment, collar, and headroom guards evaluated before any mutation; the funding rate is total via its explicit $$T = 0$$ branch ([(F.3)](/math-engine/funding-rate)); accrual accepts only strictly advancing time and positive anchor ([(F.2)](/math-engine/funding-rate)); every $$\min$$/$$\max$$ in the cascade and the fund ([(I.1)](/math-engine/insurance-fund), [(I.9)](/math-engine/insurance-fund)) is total and sign-safe; tick alignment clamps to one tick rather than producing non-positive prices ([(L.9)](/math-engine/liquidation-engine)); and the reduce-only guard ([(L.13)](/math-engine/liquidation-engine)) rejects the one known oversized-close regime before the book is touched.

## State coordinates

| Symbol            | Name                     | Scope    | Description                                                                                                                                                                                                                                                                                                                                                                                                         | Units                    |
| ----------------- | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| $$C\_a$$          | account\_collateral      | account  | Realized USDX collateral of account $$a$$: the only coordinate deposits and withdrawals touch, the cash leg of realized PnL, fees, funding, and liquidation settlement. Sign-unrestricted — engine-internal debits (funding settlement, penalties) are unguarded.                                                                                                                                                   | USDX                     |
| $$M\_{resv}$$     | reserved\_margin         | account  | Initial margin reserved by resting orders of account $$a$$, carried with the open-reservation count $$n\_{resv}$$; written at admission, released on fill or cancel, and read by the cross admission gate [(M.14)](/math-engine/margin-math) and the withdrawal guard [(M.16)](/math-engine/margin-math).                                                                                                           | USDX                     |
| $$s\_{a,k}$$      | signed\_position\_size   | position | Signed position size of account $$a$$ in market $$k$$: $$\sigma = \operatorname{sign}(s)$$, $$q =                                                                                                                                                                                                                                                                                                                   | s                        |
| $$P\_{e,a,k}$$    | entry\_price             | position | Volume-weighted entry price maintained exclusively by [(T.1)](/math-engine/position-tracker): same-side fills blend, reductions leave it untouched, flips re-seed it at the fill price. The anchor of every PnL and bankruptcy computation.                                                                                                                                                                         | USDX per base unit       |
| $$B\_k$$          | order\_book              | market   | Resting-order multiset of market $$k$$ — per order: side, limit price on the tick lattice, remaining quantity $$q - q\_f > 0$$ — with its id index in exact agreement. Uncrossed ($$P\_b < P\_a$$) in every reachable state; mutated by matching, resting, cancel/expiry, and self-trade prevention [(B.8)](/math-engine/order-book).                                                                               | multiset of orders       |
| $$A\_k$$          | accumulated\_premium     | market   | Time-weighted premium accumulator of the current funding interval, written only by [(F.2)](/math-engine/funding-rate) and reset to zero atomically at settlement.                                                                                                                                                                                                                                                   | dimensionless-seconds    |
| $$T\_k$$          | interval\_clock          | market   | Elapsed accumulated time of the current funding interval; $$T \ge 0$$ always (saturating subtraction), reset with $$A\_k$$ at settlement; the $$T = 0$$ branch of [(F.3)](/math-engine/funding-rate) keeps the rate total.                                                                                                                                                                                          | seconds                  |
| $$W\_k$$          | trade\_window            | market   | The last five executed fills $$(P^{\star}, q^{\star})$$ of market $$k$$, written by the fill map (including liquidation close fills) and read by the volume-weighted median [(O.7)](/math-engine/oracle) — the engine-owned leg of the mark blend and the channel by which the engine feeds back into its own input.                                                                                                | five (price, size) pairs |
| $$\Phi\_k$$       | insurance\_fund\_balance | system   | Per-market insurance fund balance, carried with its lifetime ledgers $$\Sigma\_{\mathrm{abs}}, \Sigma\_{\mathrm{rec}}$$ satisfying $$\Phi = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}}$$; credited by spread profit [(I.7)](/math-engine/insurance-fund) and penalties [(I.10)](/math-engine/insurance-fund), debited by absorption [(I.2)](/math-engine/insurance-fund); $$\Phi \ge 0$$ with exact depletion. | USDX                     |
| $$C\_{pool}$$     | funding\_pool            | system   | The funding pool book through which every funding transfer routes ([(S.6)](/math-engine/settlement)): positive payments flow account-to-pool, negative pool-to-account. Across the matched open interest of one settlement it nets to zero exactly.                                                                                                                                                                 | USDX                     |
| $$C\_{fee}$$      | exchange\_fee\_account   | system   | The Exchange fee account: credited taker fees, debited maker rebates ([(S.2)](/math-engine/settlement), [(S.3)](/math-engine/settlement)), with closure $$V = F - R$$ ([(S.5)](/math-engine/settlement)). Liquidation penalties never enter it — they route to the fund.                                                                                                                                            | USDX                     |
| $$P\_{oracle,k}$$ | oracle\_anchor           | market   | The trusted anchor price of market $$k$$ — the engine's input $$u\_k$$. Input-process state: moved only by the guarded accept and re-anchor promotion branches of the oracle map, never by any engine event.                                                                                                                                                                                                        | USDX per unit of asset   |
| $$t\_{last,k}$$   | anchor\_timestamp        | market   | Timestamp of the last trusted anchor update, read by the staleness predicate [(O.4)](/math-engine/oracle); frozen together with the anchor while a re-anchor is pending.                                                                                                                                                                                                                                            | milliseconds             |
| $$\Omega\_k$$     | oracle\_guard\_state     | market   | The input process's defense bookkeeping: the ten-print history $$H$$ behind [(O.2)](/math-engine/oracle) and the pending re-anchor block $$(P\_{cand}, n\_p, t\_0)$$ behind [(O.5)](/math-engine/oracle) and [(O.6)](/math-engine/oracle). Declared per the state-completeness directive; touched only by the oracle map.                                                                                           | prints and milliseconds  |

## Event dynamics

The engine is event-driven: the state sits still between events, and each event is a deterministic map $$S' = f(S, u)$$ with the oracle price $$u$$ given.

### Deposit / Withdrawal

Fires when an account moves external USDX in or out. A deposit is unguarded: any positive amount credits $$C\_a$$ and touches nothing else. A withdrawal is guarded by the flat-and-unreserved predicate [(M.16)](/math-engine/margin-math): it debits at most $$C\_a$$, and only when $$n\_{pos} = 0$$ and $$n\_{resv} = 0$$ — equity and available margin never enter the withdrawal path, so unrealized PnL can never leave the venue before a close realizes it into collateral. The guard's $$n\_{resv} = 0$$ arm is what the cancel/expiry map (below) makes reachable. Touches: $$C\_a$$ only.

$$
C\_a' = C\_a + x \quad (x > 0,\ \text{deposit}); \qquad C\_a' = C\_a - x \quad \big(0 < x \le W\_{max},\ \ W\_{max} = C\_a,\mathbb{1}\[,n\_{pos} = 0 \wedge n\_{resv} = 0,]\big) \tag{G.1}
$$

### Order submission, matching, and fill

Fires on order submission. Admission guards run first, mutating nothing on rejection: lot and tick/positivity alignment ([(B.1)](/math-engine/order-book), [(B.2)](/math-engine/order-book)), the mark collar ([(B.3)](/math-engine/order-book)), FOK availability ([(B.5)](/math-engine/order-book)), and margin headroom — the isolated path [(M.13)](/math-engine/margin-math) at the effective rate [(M.1)](/math-engine/margin-math), the cross path [(M.14)](/math-engine/margin-math) charging only added exposure ([(M.11)](/math-engine/margin-math), [(M.12)](/math-engine/margin-math)) against equity net of all outstanding reservations. Admission writes $$M\_{resv}$$ atomically with the check. The matching loop then walks price-time priority: each fill exchanges $$q^{\star} = \min(q\_t, q\_m)$$ at the maker's price ([(B.6)](/math-engine/order-book), [(B.7)](/math-engine/order-book)), bounded for market orders by the running-VWAP slippage band ([(B.9)](/math-engine/order-book), [(B.10)](/math-engine/order-book)). Self-trade prevention fires inside this loop: [(B.8)](/math-engine/order-book) decrements both same-account orders' quantities with no virtual fill, handing the fully-reduced side to the cancel map. Each fill updates positions — same-side blend [(T.1)](/math-engine/position-tracker), reduce [(T.3)](/math-engine/position-tracker) realizing [(T.4)](/math-engine/position-tracker) into $$C\_a$$ at the fill price, flip [(T.5)](/math-engine/position-tracker) — debits the taker fee and credits the maker rebate between $$C\_a$$ and $$C\_{fee}$$ ([(S.2)](/math-engine/settlement), [(S.3)](/math-engine/settlement), closure [(S.5)](/math-engine/settlement)), and appends $$(P^{\star}, q^{\star})$$ to the trade window $$W\_k$$ — the coordinate Part III's mark blend reads. Touches: $$B, M\_{resv}, s, P\_e, C\_a, C\_{fee}, W$$.

$$
\begin{aligned} q^{\star} &= \min(q\_t, q\_m), \qquad P^{\star} = P\_{mk}, \qquad W' = W \oplus (P^{\star}, q^{\star}), \qquad B' = B \ominus q^{\star}\ \text{at}\ P\_{mk} \ P\_e' &= \frac{q,P\_e + q\_f,P^{\star}}{q + q\_f}\ \ (\text{same side}), \qquad q\_c = \min(q, q\_f), \qquad q\_{new} = q\_f - q\ \ (\text{flip}) \ C\_a' &= C\_a + \sigma,(P^{\star} - P\_e),q\_c - F\_t + F\_m, \qquad C\_{fee}' = C\_{fee} + F\_t - F\_m \end{aligned} \tag{G.2}
$$

### Order cancel / expiry

Fires on user cancellation, IOC/FOK/market-remainder expiry, post-only rejection of a crossing order, slippage-cap cancellation ([(B.10)](/math-engine/order-book)), the reduce-only rejection of an oversized liquidation close ([(L.13)](/math-engine/liquidation-engine)), and self-trade prevention's cancel-of-the-fully-reduced-side ([(B.8)](/math-engine/order-book)) — this map is where the STP path lands at the composition level. It removes the order from $$B\_k$$ and its id index together (the book/index agreement is preserved by the shared removal helpers), decrements $$n\_{resv}$$, and releases the order's reserved margin from $$M\_{resv}$$. This release is load-bearing: [(M.16)](/math-engine/margin-math) guards on $$n\_{resv} = 0$$ and [(M.14)](/math-engine/margin-math) reads $$M\_{resv}$$, and both are consistent only because cancellation returns the reservation. The book-side removal is fully witnessed; the corpus contains no expression for the per-order release amount $$\mu(o)$$, so the collateral-side arithmetic of this map is a named gap (cancel\_release\_arithmetic\_unwitnessed) — nothing is cited for it. Touches: $$B, M\_{resv}, n\_{resv}$$.

$$
B' = B \setminus {o}, \qquad n\_{resv}' = n\_{resv} - 1, \qquad M\_{resv}' = M\_{resv} - \mu(o) \quad (\mu(o)\ \text{unwitnessed — named gap}) \tag{G.3}
$$

### Funding accrual

Fires on each premium sample with strictly advancing time and a strictly positive anchor; otherwise the state passes through unchanged. It computes the premium of the mark over the given input $$u$$ ([(F.1)](/math-engine/funding-rate)) and adds its time-weighted contribution to the interval accumulator ([(F.2)](/math-engine/funding-rate)). This map touches exactly two coordinates — $$(A\_k, T\_k)$$ — and no account, position, book, or fund coordinate. In particular it writes nothing into any per-position accumulator: this is the $$\varphi \equiv 0$$ convention made operational, and it is why the funding accumulator is not a state coordinate of this model.

$$
A' = A + \frac{m\_k - u\_k}{u\_k},\Delta t, \qquad T' = T + \Delta t \qquad (\Delta t > 0,\ u\_k > 0); \qquad \text{all other coordinates unchanged} \tag{G.4}
$$

### Funding settlement

Fires at the end of each funding interval. The rate is the clamped time-weighted average premium with the total $$T = 0$$ branch ([(F.3)](/math-engine/funding-rate)); each open position's payment is $$\Pi^f = \sigma, q, m\_k, f$$ ([(F.4)](/math-engine/funding-rate)), computed fresh from $$(A, T)$$ — settle-and-reset, never read from accrued per-position state. Each payment routes through the funding pool as a transfer of magnitude $$|\Pi^f|$$ with sign-selected direction ([(S.6)](/math-engine/settlement)): the account leg debits or credits $$C\_a$$, the pool leg mirrors it in $$C\_{pool}$$. The collateral debit is **unguarded** — no margin check precedes it, so settlement can push an account through its maintenance floor with no input motion (one of the three engine-internal channels scoped into the margin-monotonicity invariant). The interval pair resets atomically: $$(A', T') = (0, 0)$$. Touches: $$C\_a$$ for every account with an open position, $$C\_{pool}$$, $$(A\_k, T\_k)$$.

$$
f = \begin{cases} 0 & T = 0 \ \operatorname{clamp}!\big(A/T,, -c,, +c\big) & T > 0 \end{cases}; \qquad C\_a' = C\_a - \Pi^f\_a,\ \ \Pi^f\_a = \sigma\_a, q\_a, m\_k, f; \qquad C\_{pool}' = C\_{pool} + \textstyle\sum\_a \Pi^f\_a; \qquad (A', T') = (0, 0) \tag{G.5}
$$

### Liquidation cascade

Fires when an account's fresh-mark equity falls to its maintenance floor: the inclusive trigger $$E \le \sum\_i M\_i$$ ([(L.4)](/math-engine/liquidation-engine)) over [(L.1)](/math-engine/liquidation-engine), [(L.2)](/math-engine/liquidation-engine), [(L.3)](/math-engine/liquidation-engine). Collateral is apportioned — cross accounts split the shared pool loss-proportionally with the remainder fold conserving it exactly ([(L.6)](/math-engine/liquidation-engine), [(L.7)](/math-engine/liquidation-engine)); isolated positions use their stamped cushion ([(L.5)](/math-engine/liquidation-engine)). Close orders are sized ([(L.11)](/math-engine/liquidation-engine), [(L.12)](/math-engine/liquidation-engine)), guarded reduce-only ([(L.13)](/math-engine/liquidation-engine) — a negative share yields an oversized order that is rejected each scan, a stall not a flip), priced from the bankruptcy price ([(L.8)](/math-engine/liquidation-engine)) tick-aligned toward executability ([(L.9)](/math-engine/liquidation-engine), [(L.10)](/math-engine/liquidation-engine)), and executed through the same matching map as any order — their fills enter $$W\_k$$. Settlement per market: $$X\_i = s\_i + \Pi\_{\mathrm{fill},i} + \Pi\_{\mathrm{res},i} - g\_i$$ with realized fill PnL [(L.15)](/math-engine/liquidation-engine), mark-valued residual [(L.16)](/math-engine/liquidation-engine), and spread profit [(L.14)](/math-engine/liquidation-engine); bad debt is $$D\_i = \max(0, -X\_i)$$ ([(L.17)](/math-engine/liquidation-engine)). The liquidated account's collateral is defined explicitly: $$C\_a' = \sum\_i (\max(X\_i, 0) - \Lambda\_i)$$, where the penalty $$\Lambda\_i$$ is the owed amount [(S.4)](/math-engine/settlement) capped at the retained collateral ([(I.9)](/math-engine/insurance-fund)) — a debit whose fund-side destination is witnessed: [(I.10)](/math-engine/insurance-fund) and [(I.11)](/math-engine/insurance-fund) credit the same $$\Lambda$$ to $$\Phi$$ in one atomic no-mint pair (v0.0.9 expressions closing the formerly-open liquidation\_penalty\_sink finding). Fund sequencing is credit-before-absorb for BOTH credits (settled, code-verified 2026-07-12: apply\_liquidation\_penalty precedes fill accounting in execute\_liquidation, per\_market.rs:552-574, and the spread credit precedes absorb\_bad\_debt): $$\Phi\_1 = \Phi + \Lambda + g$$ ([(I.10)](/math-engine/insurance-fund), [(I.7)](/math-engine/insurance-fund)), then absorption $$\min(D, \Phi\_1)$$ ([(I.1)](/math-engine/insurance-fund), [(I.2)](/math-engine/insurance-fund)) with lockstep ledgers ([(I.3)](/math-engine/insurance-fund), [(I.8)](/math-engine/insurance-fund)). ADL arms through two **nested** predicates: the settle amount $$D\_{\mathrm{adl}} = \max(D - \Phi\_1, 0) > 0$$ ([(I.4)](/math-engine/insurance-fund)) and, after absorption, the threshold $$\Phi' \le \kappa$$ ([(I.5)](/math-engine/insurance-fund)). Settle-amount firing implies threshold firing ($$D\_{\mathrm{adl}} > 0$$ forces $$\Phi' = 0 \le \kappa$$); the divergence is one-sided — e.g. $$\Phi\_1 = 12, D = 5, \kappa = 10$$ gives $$D\_{\mathrm{adl}} = 0$$ yet $$\Phi' = 7 \le \kappa$$: armed with nothing to settle. Whether ADL executes in that regime is the open finding adl\_arming\_condition\_ambiguity. Counterparties rank by $$\rho = \pi L$$ ([(L.18)](/math-engine/liquidation-engine), [(I.6)](/math-engine/insurance-fund)); the counterparty settlement map itself is unmodeled — a named gap (adl\_settlement\_unmodeled). One further seam is named rather than hidden: on a partial fill the settle map counts $$\Pi\_{\mathrm{res}}$$ as cash in $$X$$ while the surviving remainder retains entry $$P\_e$$ ([(T.1)](/math-engine/position-tracker) is untouched by reductions) — no expression re-marks the residual's entry, so the partial-fill case double-carries that PnL across layers (residual\_entry\_remark\_unwitnessed). Touches: $$s, P\_e, B, W, C\_a, \Phi, \Sigma\_{\mathrm{abs}}, \Sigma\_{\mathrm{rec}}, C\_{fee}$$. The fee treatment of liquidation close fills (whether the liquidatee is charged taker fees, and resting counterparties earn rebates, on cascade fills) is unwitnessed by any component expression; $$C\_{fee}$$ is therefore excluded from this map's write set and the question is a named gap (liquidation\_fill\_fee\_treatment).

$$
\begin{aligned} &\text{fire: } E \le \textstyle\sum\_i M\_i; \qquad s\_i = C,\tfrac{\ell\_i}{\mathcal{L}}\ (+\ \text{remainder fold}), \qquad X\_i = s\_i + \Pi\_{\mathrm{fill},i} + \Pi\_{\mathrm{res},i} - g\_i, \qquad D = \textstyle\sum\_i \max(0, -X\_i) \ \&C\_a' = \textstyle\sum\_i \big( \max(X\_i, 0) - \Lambda\_i \big), \qquad \Lambda\_i = \min!\big(\Lambda\_{\mathrm{owed},i},\ \max(X\_i, 0)\big) \ &\Phi\_1 = \Phi + \Lambda + g, \qquad \Phi' = \max(\Phi\_1 - D,\ 0), \qquad D\_{\mathrm{adl}} = \max(D - \Phi\_1,\ 0) \end{aligned} \tag{G.6}
$$

## Invariants of the engine

### Aggregate funding zero-sum across matched open interest

$$
\sum\_{a} \Pi^{f}*{a} ;=; f, m\_k \sum*{a} s\_{a,k} ;=; 0 \qquad\Longrightarrow\qquad C\_{pool}' = C\_{pool} \tag{G.7}
$$

Within each market, a funding settlement transfers value between the sides but creates none: the payments [(F.4)](/math-engine/funding-rate) sum to zero over all accounts because open interest is matched ($$\sum\_a s\_{a,k} = 0$$), so the pool's net position across the settlement's transfers ([(S.6)](/math-engine/settlement)) is exactly zero.

*Why it holds:* Position coordinates are written only by fill maps, and every fill adjusts taker and maker by the same $$q^{\star}$$ with opposite signs ([(B.6)](/math-engine/order-book), with reduces and flips per [(T.3)](/math-engine/position-tracker) and [(T.5)](/math-engine/position-tracker)) — so $$\sum\_a s\_{a,k} = 0$$ inductively from the empty market; deposits, withdrawals, cancels, and accrual never touch $$s$$, and liquidation closes execute through the same fill map. The payment is linear in signed size with the common factor $$f, m\_k$$ ([(F.4)](/math-engine/funding-rate)), so the account legs cancel exactly in decimal arithmetic, and each pool transfer carries exactly the account leg's magnitude with mirrored direction ([(S.6)](/math-engine/settlement)).

*Composes:* [*(F.4)*](/math-engine/funding-rate) [*(F.3)*](/math-engine/funding-rate) [*(S.6)*](/math-engine/settlement) [*(B.6)*](/math-engine/order-book) [*(T.3)*](/math-engine/position-tracker) [*(T.5)*](/math-engine/position-tracker)

### Collateral conservation through the liquidation cascade

$$
\sum\_i s\_i = C, \qquad D = \min(D, \Phi\_1) + \max(D - \Phi\_1,\ 0), \qquad \Delta C\_a\big|*{\Lambda} = -\Lambda = -\Delta\Phi\big|*{\Lambda}, \qquad \Phi = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}} \tag{G.8}
$$

Through a fully-filled cascade, equity is apportioned and transferred, never created: the cross pool splits into shares that sum back to it exactly ([(L.6)](/math-engine/liquidation-engine), [(L.7)](/math-engine/liquidation-engine)); every unit of bad debt [(L.17)](/math-engine/liquidation-engine) is covered exactly once, split between fund absorption [(I.1)](/math-engine/insurance-fund) and the ADL settle amount [(I.4)](/math-engine/insurance-fund) at the post-spread-credit balance $$\Phi\_1$$; and the penalty is a no-mint transfer — the account debit and the fund credit are the same capped $$\Lambda$$ ([(I.9)](/math-engine/insurance-fund), [(I.10)](/math-engine/insurance-fund)). Honest scope: stated for fully-filled liquidations ($$Q\_f = q$$, $$\Pi\_{\mathrm{res}} = 0$$); the partial-fill case is unreconciled by the residual entry-remark gap named in the composition, and the ADL leg conserves only up to the unmodeled counterparty settlement.

*Why it holds:* The remainder fold adds $$C - \sum\_j s\_j$$ to exactly one share, restoring $$\sum\_i s\_i = C$$ identically regardless of rounding. The $$\min/\max$$ pair is a complementary split of $$D$$ at $$\Phi\_1$$, and credit-before-absorb makes $$\Phi\_1 = \Phi + g$$ the balance absorption actually reads ([(I.7)](/math-engine/insurance-fund) commits before [(I.2)](/math-engine/insurance-fund)). The penalty legs are one atomic mutation carrying a single $$\Lambda$$ capped at available collateral, so neither side can exceed the other. Fund closure $$\Phi = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}}$$ holds because every fund update moves the balance and exactly one ledger by the same amount.

*Composes:* [*(L.6)*](/math-engine/liquidation-engine) [*(L.7)*](/math-engine/liquidation-engine) [*(L.17)*](/math-engine/liquidation-engine) [*(I.1)*](/math-engine/insurance-fund) [*(I.4)*](/math-engine/insurance-fund) [*(I.7)*](/math-engine/insurance-fund) [*(I.2)*](/math-engine/insurance-fund) [*(I.9)*](/math-engine/insurance-fund) [*(I.10)*](/math-engine/insurance-fund)

### Margin monotonicity (maintenance strictly inside initial)

$$
r\_m < r\_i ;\Longrightarrow; \big{, S : E \le M\_{maint}^{pf} ,\big} \subsetneq \big{, S : E \le M\_{init}^{pf} ,\big} \tag{G.9}
$$

For every admissible parameter set, maintenance margin is strictly below initial margin position-wise and in the portfolio sums ([(M.4)](/math-engine/margin-math) vs [(M.2)](/math-engine/margin-math), [(M.10)](/math-engine/margin-math) vs [(M.9)](/math-engine/margin-math)), so the liquidation region [(L.4)](/math-engine/liquidation-engine) is strictly contained in the admission-blocked region and a just-admitted order sits strictly above its maintenance floor at the admission mark. Honest behavioral scope: this is a *geometric* buffer, not a temporal guarantee — three engine-internal channels can close the gap with no input motion: the unguarded funding-settlement collateral debit ([(F.4)](/math-engine/funding-rate)), the taker-fee debit that admission headroom does not charge ([(S.2)](/math-engine/settlement) vs [(M.14)](/math-engine/margin-math)), and trade-leg mark motion from the engine's own fills entering the blend (findings: unguarded funding debit, admission fee gap, trade-leg self-influence).

*Why it holds:* Both requirements are the product $$q \cdot P \cdot r$$ differing only in the rate, and $$r\_m < r\_i$$ is validated at parameter parse, so the per-position inequality is strict and survives summation over the identical position set. The effective rate only widens the gap ($$r\_{eff} \ge r\_i$$, [(M.1)](/math-engine/margin-math)). No engine map weakens the inclusion — the three gap-closing channels move $$E$$, not the region ordering — so the invariant is preserved by every event while the behavioral contract is scoped to the cited channels.

*Composes:* [*(M.4)*](/math-engine/margin-math) [*(M.2)*](/math-engine/margin-math) [*(M.10)*](/math-engine/margin-math) [*(M.9)*](/math-engine/margin-math) [*(M.1)*](/math-engine/margin-math) [*(M.14)*](/math-engine/margin-math) [*(L.4)*](/math-engine/liquidation-engine) [*(S.2)*](/math-engine/settlement) [*(F.4)*](/math-engine/funding-rate)

### Bounded funding transfer per interval

$$
\lvert \Pi^{f}\_{a} \rvert ;\le; c, q\_a, m\_k \qquad \text{per position, per funding interval} \tag{G.10}
$$

No funding settlement can move more than the cap fraction of a position's mark notional in one interval: the rate is clamped to $$\[-c, +c]$$ on both of its branches ([(F.3)](/math-engine/funding-rate)), and the payment is linear in the rate ([(F.4)](/math-engine/funding-rate)), so each account's per-position debit or credit is bounded by $$c, q, m\_k$$ and the pool's gross throughput by $$c, m\_k \sum\_a q\_a$$.

*Why it holds:* The rate map has exactly two return paths: $$T = 0$$ returns zero, and $$T > 0$$ applies the clamp directly, so $$|f| \le c$$ in every reachable state. The payment map multiplies $$f$$ by $$\sigma, q, m\_k$$ with no other rate dependence, and the settlement transfer carries exactly $$|\Pi^f|$$ ([(S.6)](/math-engine/settlement)) — no map between the clamp and the ledger can amplify the amount.

*Composes:* [*(F.3)*](/math-engine/funding-rate) [*(F.4)*](/math-engine/funding-rate) [*(S.6)*](/math-engine/settlement)

### Fee closure across the fill path

$$
\Delta C\_{fee} ;=; F - R ;=; V, \qquad \Delta C\_{fee} + \textstyle\sum\_a \Delta C\_a \big|\_{\text{fees}} = 0 \tag{G.11}
$$

Across any batch of fills, the fee flows between accounts and the Exchange fee account close exactly: every taker fee debited from an account is either paid out as a maker rebate or retained as revenue ([(S.2)](/math-engine/settlement), [(S.3)](/math-engine/settlement), [(S.5)](/math-engine/settlement)), the closure is preserved linearly under batch merge ([(S.7)](/math-engine/settlement)), and liquidation penalties never contaminate it — they route to the fund, never to $$C\_{fee}$$ ([(S.4)](/math-engine/settlement)).

*Why it holds:* Both accumulators are built fill-by-fill from the identical truncated amounts placed into the transfer ledger, and revenue is computed once as their difference — there is no independent revenue path to drift. The account-side legs are the same $$F\_t$$ and $$F\_m$$ the fill map applies to $$C\_a$$, so the system-wide fee sum telescopes to zero. Merge only adds accumulators and concatenates transfers, so closure survives batching; the penalty push site targets the insurance fund and never enters $$F$$, $$R$$, or $$V$$.

*Composes:* [*(S.2)*](/math-engine/settlement) [*(S.3)*](/math-engine/settlement) [*(S.5)*](/math-engine/settlement) [*(S.7)*](/math-engine/settlement) [*(S.4)*](/math-engine/settlement) [*(S.1)*](/math-engine/settlement)

## Structure of the flow

**Geometry of the healthy region.** For a fixed position set, equity is affine in each mark with slope the net signed size, $$\partial E / \partial m\_k = \sum\_i s\_{i,k}$$ ([(M.8)](/math-engine/margin-math)), while total maintenance is piecewise-linear with slope $$\sum\_i q\_{i,k}, r\_{m,k}$$ ([(M.10)](/math-engine/margin-math)) — so the healthy region $${S : E > \sum\_i M\_i}$$ is, in the mark coordinate of a single-market account, a half-line whose boundary is exactly the displayed liquidation price ([(T.8)](/math-engine/position-tracker)), with the bankruptcy price ([(T.9)](/math-engine/position-tracker)) strictly beyond it for any under-collateralized position. Margin requirements scale linearly in size ([(M.2)](/math-engine/margin-math)), the effective rate never undercuts the market rate ([(M.1)](/math-engine/margin-math)), added exposure is non-negative so admission never pre-credits a reduce ([(M.11)](/math-engine/margin-math)), and the slippage cap is monotone in its band ([(B.10)](/math-engine/order-book)).

**Fixed points under constant input.** Hold $$u$$ constant with an empty or agreeing trade window (so $$m\_k = P\_{oracle,k}$$) and suppress user events. Then the premium vanishes ([(F.1)](/math-engine/funding-rate)), accrual adds $$0 \cdot \Delta t$$ to $$A$$ while only the clock $$T$$ advances, settlement computes $$f = 0$$ and emits no transfer ([(F.3)](/math-engine/funding-rate), [(S.6)](/math-engine/settlement)), and the trigger stays silent on every healthy account: all economic coordinates $$(C\_a, s, P\_e, B, \Phi, C\_{pool}, C\_{fee})$$ are fixed, and the funding pair $$(A, T)$$ cycles through zero-amount settlements. Healthy states at an agreeing mark are thus equilibria of the autonomous engine. The interesting non-equilibria are one-sided: once $$E \le \sum M\_i$$, the cascade fires and the account's position coordinates contract monotonically toward flat; and because the trigger is inclusive ([(L.4)](/math-engine/liquidation-engine)), the boundary itself belongs to the liquidation region, not the healthy one.

## Appendix — the complete formal system

This appendix is rendered mechanically from the state-space classification (`models/state-space.json`) — derived, not written. Its completeness claim is *checked*: the closure gate (`ci/closure.py`) verifies on every run that every corpus expression is classified, every state coordinate is written by an event and read somewhere, and every event map cites only defined coordinates. The state space factors into 16 coordinates (global fund and cash books; per-market book, trade window, premium accumulator, oracle anchor and re-anchor pending block; per-account collateral and reserved margin; per-position size/entry/direction and funding accumulator), 9 inputs, 20 parameters, and 54 derived observables, with all 133 corpus variables and 84 expressions classified totally across 10 events. Alphabet completion added adl\_execution, oracle\_reanchor\_step, oracle\_reanchor\_commit — including adl\_execution emitted with empty writes so the closure gate keeps flagging the unmodeled ADL counterparty settlement map. Deliberately uncited update maps (deposit/withdrawal transfer, cancel removal and reservation release, anchor assignments, trade-window push, funding resets) remain named gaps rather than inventions.

### A.1 The state vector

**global**

| Coordinate             | Symbol        | Units | Owner      | Description                                                                                                                                                                                                                                                                  |
| ---------------------- | ------------- | ----- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `funding_pool`         | $$C\_{pool}$$ | USDX  | settlement | The funding pool book through which every funding transfer routes; nets to zero exactly across the matched open interest of one settlement. No component variable persists it — declared by the global composition; its only witnessed update is the transfer-magnitude map. |
| `exchange_fee_account` | $$C\_{fee}$$  | USDX  | settlement | The Exchange fee account, carried with its settlement-record accumulators (total fees F, total rebates R, net revenue V = F - R, and their batch-merge operands): credited taker fees, debited maker rebates; liquidation penalties never enter it.                          |

**per-market**

| Coordinate                      | Symbol                     | Units                                                                     | Owner          | Description                                                                                                                                                                                                                                                              |
| ------------------------------- | -------------------------- | ------------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `order_book`                    | $$B\_k$$                   | multiset of orders (side, limit price, remaining quantity, fill progress) | order-book     | Resting-order multiset of the market with its id index in exact agreement; per-order fill progress (filled\_qty) lives inside this composite; uncrossed (P\_b < P\_a) in every reachable state.                                                                          |
| `accumulated_premium`           | $$A\_k$$                   | dimensionless-seconds                                                     | funding-rate   | Time-weighted premium accumulator of the current funding interval, written only by the sample-contribution map and reset to zero atomically at settlement.                                                                                                               |
| `interval_clock`                | $$T\_k$$                   | seconds                                                                   | funding-rate   | Elapsed accumulated time of the current funding interval, advanced with each sample and reset with A\_k at settlement; the T = 0 branch of the rate keeps the map total. (The component text calls it derived; the composed system persists it as the interval's clock.) |
| `trade_window`                  | $$W\_k$$                   | five (price, size) pairs                                                  | oracle         | The last five executed fills of the market, written by the fill map (including liquidation close fills) and read by the volume-weighted median trade reference — the engine-owned leg of the mark blend.                                                                 |
| `insurance_fund_balance`        | $$\Phi\_k$$                | USDX                                                                      | insurance-fund | Per-market insurance fund balance: credited by spread profit and penalties, debited by bad-debt absorption; Phi >= 0 with exact depletion, and Phi = Sigma\_rec - Sigma\_abs against its lifetime ledgers.                                                               |
| `insurance_fund_total_absorbed` | $$\Sigma\_{\mathrm{abs}}$$ | USDX                                                                      | insurance-fund | Lifetime bad-debt absorption ledger of the fund; monotonically non-decreasing, incremented in lockstep with each absorption.                                                                                                                                             |
| `insurance_fund_total_received` | $$\Sigma\_{\mathrm{rec}}$$ | USDX                                                                      | insurance-fund | Lifetime receipts ledger of the fund (initial balance plus spread profits and penalty credits); monotonically non-decreasing, moving in lockstep with balance credits. The initial-balance credit has no event in the alphabet (initialization, not dynamics).           |
| `oracle_anchor`                 | $$P\_{oracle,k}$$          | USDX per unit of asset                                                    | oracle         | The trusted anchor price of the market — the engine's exogenous input process state; moved only by the guarded fresh-accept and re-anchor-commit branches of the oracle map, never by any engine event.                                                                  |
| `anchor_timestamp`              | $$t\_{last,k}$$            | milliseconds                                                              | oracle         | Timestamp of the last trusted anchor update, read by the staleness predicate; frozen together with the anchor while a re-anchor is pending.                                                                                                                              |
| `oracle_guard_state`            | $$\Omega\_k$$              | prints and milliseconds                                                   | oracle         | The input process's defense bookkeeping: the ten-print history (whose oldest element the path check reads) and the pending re-anchor block (candidate price, pending print count, confirmation counter, opening timestamp); touched only by the oracle map.              |

**per-account**

| Coordinate           | Symbol        | Units | Owner       | Description                                                                                                                                                                                                          |
| -------------------- | ------------- | ----- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account_collateral` | $$C\_a$$      | USDX  | margin-math | Realized USDX collateral of an account: the cash leg of deposits/withdrawals, realized PnL, fees, funding, and liquidation settlement; sign-unrestricted because engine-internal debits are unguarded.               |
| `reserved_margin`    | $$M\_{resv}$$ | USDX  | margin-math | Initial margin reserved by resting/in-flight orders, carried with the open-reservation count n\_{resv}; written at admission, released on fill or cancel, read by the cross admission gate and the withdrawal guard. |

**per-position**

| Coordinate             | Symbol         | Units              | Owner            | Description                                                                                                                                                                                   |
| ---------------------- | -------------- | ------------------ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `signed_position_size` | $$s\_{a,k}$$   | base units, signed | position-tracker | Signed position size (sigma = sign(s), q = \|s\| > 0 for a stored position, \|s\| a lot multiple); written only by the fill map (open, increase, reduce, flip) and the cascade's close fills. |
| `entry_price`          | $$P\_{e,a,k}$$ | USDX per base unit | position-tracker | Volume-weighted entry price maintained exclusively by the VWAP blend: same-side fills blend, reductions leave it untouched, flips re-seed it at the fill price.                               |

### A.2 Inputs and parameters

Inputs are exogenous — they arrive from outside the state; parameters are constants of market or system configuration.

**Inputs**

| Input                      | Symbol       | Units                  | Description                                                                                                                     |
| -------------------------- | ------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `oracle_print_price`       | $$P\_{new}$$ | USDX per unit of asset | An incoming external oracle print being validated against the anchor or the pending re-anchor candidate.                        |
| `wall_clock_time`          | $$t\_{now}$$ | milliseconds           | Feed-supplied timestamp of the incoming print or staleness evaluation, in Unix milliseconds.                                    |
| `time_delta`               | $$\Delta t$$ | seconds                | Exogenous elapsed time since the previous funding premium sample; non-advancing samples are ignored.                            |
| `order_quantity`           | $$q$$        | base units             | Quantity of an arriving order; must be a lot multiple to pass admission.                                                        |
| `order_limit_price`        | $$P\_{lim}$$ | USDX per base unit     | Limit price of an arriving limit order; must be strictly positive, tick-aligned, and inside the mark collar.                    |
| `order_signed_size`        | $$o$$        | base units, signed     | Signed size of an arriving order (buy positive, sell negative), read by the added-exposure computation.                         |
| `max_slippage_bps`         | $$\beta$$    | basis points           | Taker-supplied per-order slippage cap on a market order; absent means no cap.                                                   |
| `preview_requested_qty`    | $$q\_{req}$$ | base units             | Quantity requested by a hypothetical market order in the read-only VWAP preview; undefined for non-positive requests.           |
| `external_transfer_amount` | $$x$$        | USDX                   | External USDX amount of a deposit or withdrawal; not a corpus variable — carried by the composition's deposit/withdrawal event. |

**Parameters**

| Parameter                    | Symbol        | Units                         | Scope       | Description                                                                                                                                                                             |
| ---------------------------- | ------------- | ----------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `funding_rate_cap`           | $$c$$         | fraction per funding interval | per-market  | Symmetric cap on the funding rate; specified at 0.5% per settlement window.                                                                                                             |
| `adl_threshold`              | $$\kappa$$    | USDX                          | per-market  | ADL trigger threshold on the fund balance; default zero arms ADL only at full depletion.                                                                                                |
| `maintenance_margin_rate`    | $$r\_m$$      | dimensionless ratio           | per-market  | Market maintenance margin rate; strictly less than the initial margin rate.                                                                                                             |
| `initial_margin_rate`        | $$r\_i$$      | dimensionless ratio           | per-market  | Market initial margin rate, equal to one over the market's maximum leverage.                                                                                                            |
| `account_leverage`           | $$L$$         | multiplier                    | per-account | Account-selected leverage per market (integer >= 1, validated before storage); user configuration with no state-mutating expression in the corpus, hence a parameter, not a coordinate. |
| `tick_size`                  | $$\delta$$    | USDX per base unit            | per-market  | Minimum price increment; non-positive tick disables alignment.                                                                                                                          |
| `lot_size`                   | $$\ell$$      | base units                    | per-market  | Market lot size; order and position sizes are integer multiples of it; zero disables the alignment check.                                                                               |
| `taker_fee_bps`              | $$b\_t$$      | basis points                  | per-market  | Taker fee rate charged on fill notional.                                                                                                                                                |
| `maker_rebate_bps`           | $$b\_m$$      | basis points                  | per-market  | Maker rebate rate, stored negative by convention; applied by absolute value.                                                                                                            |
| `liquidation_penalty_bps`    | $$b\_{liq}$$  | basis points                  | per-market  | Penalty rate applied to the notional of liquidation fills and routed to the insurance fund.                                                                                             |
| `price_band_bps`             | $$b\_{band}$$ | basis points                  | per-market  | Maximum admissible relative deviation of a limit price from the mark (the admission collar).                                                                                            |
| `oracle_deviation_threshold` | $$\theta$$    | dimensionless fraction        | per-market  | Single-step deviation threshold for accepting an oracle print against the anchor.                                                                                                       |
| `oracle_history_size`        | $$N\_h$$      | prints                        | global      | Fixed size of the rolling price-update window used by the path-manipulation check (HISTORY\_SIZE = 10).                                                                                 |
| `oracle_staleness_seconds`   | $$\tau\_s$$   | seconds                       | per-market  | Staleness threshold: the anchor is stale strictly beyond this many seconds since the last trusted update.                                                                               |
| `reanchor_max_deviation`     | $$\theta\_r$$ | dimensionless fraction        | per-market  | Per-step consistency bound for re-anchor confirmations against the running candidate.                                                                                                   |
| `escalation_max_deviation`   | $$\theta\_e$$ | dimensionless fraction        | per-market  | Widened per-step bound applied once the escalation trigger has fired.                                                                                                                   |
| `required_confirmations`     | $$k$$         | prints                        | per-market  | Consecutive mutually-consistent prints required to promote a re-anchor; floored at 2 effectively.                                                                                       |
| `escalation_prints`          | $$N\_e$$      | prints                        | per-market  | Print-count arm of the re-anchor escalation trigger.                                                                                                                                    |
| `escalation_seconds`         | $$\tau\_e$$   | seconds                       | per-market  | Wall-clock arm of the re-anchor escalation trigger, measured from the opening of the pending sequence.                                                                                  |
| `oracle_mark_weight`         | $$w$$         | dimensionless fraction        | per-market  | Oracle weight in the mark blend; unit-interval, default 0.95 (oracle-dominant).                                                                                                         |

### A.3 Derived observables

Pure functions of state, inputs, and parameters — recomputed, never persisted.

| Quantity                   | Symbol              | Units                 | Defined by                                | Description                                                                                                                                                                                                                |
| -------------------------- | ------------------- | --------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mark_price`               | $$m\_k$$            | USDX per base unit    | [(O.8)](/math-engine/oracle)              | Convex blend of the trusted anchor and the volume-weighted trade reference; equals the anchor when no trade reference exists. Recomputed on demand, never persisted.                                                       |
| `trade_ref`                | $$P\_{trade}$$      | USDX per base unit    | [(O.7)](/math-engine/oracle)              | Volume-weighted median price of the five-trade window; falls back to the last trade price when the window is short.                                                                                                        |
| `premium_index`            | $$p$$               | dimensionless         | [(F.1)](/math-engine/funding-rate)        | Fractional deviation of the mark from the anchor at a sample; skipped when the anchor is non-positive.                                                                                                                     |
| `funding_rate`             | $$f$$               | fraction per interval | [(F.3)](/math-engine/funding-rate)        | Clamped time-weighted average premium, recomputed fresh from (A, T) at settlement with the T = 0 branch returning zero.                                                                                                    |
| `funding_payment`          | $$\Pi^f$$           | USDX                  | [(F.4)](/math-engine/funding-rate)        | Signed per-position funding payment sigma q m f; simultaneously the settlement event's collateral update delta.                                                                                                            |
| `unrealized_pnl`           | $$\mathrm{uPnL}$$   | USDX                  | [(T.2)](/math-engine/position-tracker)    | Per-position mark-to-market PnL; the account total is its sum over open positions (coherence-merged with liquidation-engine.fresh\_unrealized\_pnl).                                                                       |
| `account_equity`           | $$E$$               | USDX                  | [(M.8)](/math-engine/margin-math)         | Collateral plus mark-to-market PnL (net of funding integrals in the portfolio form); coherence-merged across margin-math.equity, margin-math.portfolio\_equity, and liquidation-engine.account\_equity.                    |
| `maintenance_margin`       | $$M\_m$$            | USDX                  | [(M.4)](/math-engine/margin-math)         | Maintenance margin at the mark; coherence-merged with the portfolio and liquidation-engine instances.                                                                                                                      |
| `initial_margin`           | $$M\_i$$            | USDX                  | [(M.2)](/math-engine/margin-math)         | Initial margin at the mark (stamped allocated margin where set); coherence-merged with the portfolio instance.                                                                                                             |
| `available_margin`         | $$M\_{avail}$$      | USDX                  | [(M.7)](/math-engine/margin-math)         | Equity minus total initial margin held; can be negative; gates order admission, not withdrawal.                                                                                                                            |
| `added_exposure`           | $$\Delta q$$        | base units            | [(M.11)](/math-engine/margin-math)        | Magnitude of newly-opened exposure an order adds: growth on increase, zero on reduce/close, the whole new side on a flip.                                                                                                  |
| `admission_added_margin`   | $$M\_{add}$$        | USDX                  | [(M.12)](/math-engine/margin-math)        | Initial margin charged on added exposure at the mark and effective rate; also the amount written into the reservation at admission.                                                                                        |
| `isolated_margin_cushion`  | $$C\_{iso}$$        | USDX                  | [(L.5)](/math-engine/liquidation-engine)  | Collateral backing an isolated position: the margin allocated at fill time if recorded, otherwise the open-time initial margin at market rate; the trigger ((L.4)) and the liquidation pricing both use this single value. |
| `bankruptcy_price`         | $$p\_b$$            | USDX per base unit    | [(L.8)](/math-engine/liquidation-engine)  | Price at which the position's backing collateral is exactly exhausted; coherence-merged with position-tracker.bankruptcy\_price; may be zero or negative before alignment.                                                 |
| `aligned_bankruptcy_price` | $$\tilde{p}\_b$$    | USDX per base unit    | [(L.9)](/math-engine/liquidation-engine)  | Tick-aligned close-order limit: floor for sells (liquidation-engine.aligned\_price\_sell), ceil for buys (liquidation-engine.aligned\_price\_buy), clamped to one tick.                                                    |
| `liquidation_price`        | $$p\_{liq}$$        | USDX per base unit    | [(T.8)](/math-engine/position-tracker)    | Analytically-solved mark at which equity meets the maintenance requirement; display/analysis, neither moves state nor gates events.                                                                                        |
| `cross_collateral_share`   | $$s\_i$$            | USDX                  | [(L.6)](/math-engine/liquidation-engine)  | Loss-proportional share of the shared cross pool per market, with the remainder folded into the largest-loss position's share.                                                                                             |
| `collateral_share_sum`     | $$S$$               | USDX                  | \`\`                                      | Sum of the proportional shares before the remainder fold; prose-defined only — no corpus expression id (read by the remainder fold).                                                                                       |
| `position_loss`            | $$\ell\_i$$         | USDX                  | \`\`                                      | max(0, -uPnL\_i) per market with entry-price fallback; defined only in variable prose, no corpus expression id.                                                                                                            |
| `total_loss`               | $$\mathcal{L}$$     | USDX                  | \`\`                                      | Sum of position losses across the positions liquidated together; prose-defined only.                                                                                                                                       |
| `safe_size`                | $$q\_{safe}$$       | base units            | [(L.11)](/math-engine/liquidation-engine) | Largest lot-multiple size whose 1.5x-padded initial margin the collateral share covers.                                                                                                                                    |
| `liquidation_qty`          | $$q\_{liq}$$        | base units            | [(L.12)](/math-engine/liquidation-engine) | Close-order quantity: full size in Full mode or degenerate cases, else reduction to safe size.                                                                                                                             |
| `fill_quantity`            | $$q^{\star}$$       | base units            | [(B.6)](/math-engine/order-book)          | Quantity of a single fill: min of taker and front-maker remainders; coherence-merged with settlement.size and position-tracker.fill\_quantity.                                                                             |
| `fill_price`               | $$P^{\star}$$       | USDX per base unit    | [(B.7)](/math-engine/order-book)          | Price of a single fill — always the maker's limit price; coherence-merged with settlement.price and position-tracker/liquidation-engine fill prices.                                                                       |
| `closed_quantity`          | $$q\_c$$            | base units            | [(T.3)](/math-engine/position-tracker)    | Portion of an opposing fill that closes existing size: min(size, fill quantity).                                                                                                                                           |
| `total_filled`             | $$Q\_f$$            | base units            | \`\`                                      | Sum of the liquidation close order's fill quantities; within-event accumulation, prose-defined only.                                                                                                                       |
| `spread_profit`            | $$g$$               | USDX                  | [(L.14)](/math-engine/liquidation-engine) | Positive part of fills' price improvement over the aligned bankruptcy price, summed over fills; credited to the fund.                                                                                                      |
| `realized_fill_pnl`        | $$\Pi\_{fill}$$     | USDX                  | [(L.15)](/math-engine/liquidation-engine) | Signed realized PnL of liquidation fills, each at its own fill price; a component of the cascade's per-market settlement X\_i.                                                                                             |
| `residual_unfilled_pnl`    | $$\Pi\_{res}$$      | USDX                  | [(L.16)](/math-engine/liquidation-engine) | Mark-valued PnL of the unfilled remainder of the close order; zero on complete fill.                                                                                                                                       |
| `bad_debt`                 | $$D$$               | USDX                  | [(L.17)](/math-engine/liquidation-engine) | Non-negative shortfall after fills-aware settlement against the collateral share; drawn from the fund, then ADL.                                                                                                           |
| `absorbed_amount`          | $$D\_{abs}$$        | USDX                  | [(I.1)](/math-engine/insurance-fund)      | min(bad debt, fund balance): the delta by which the fund and its absorption ledger move.                                                                                                                                   |
| `adl_settle_amount`        | $$D\_{adl}$$        | USDX                  | [(I.4)](/math-engine/insurance-fund)      | Shortfall handed to ADL after the fund is drained; an instruction is emitted only when strictly positive.                                                                                                                  |
| `adl_priority_score`       | $$\rho$$            | dimensionless         | [(L.18)](/math-engine/liquidation-engine) | ADL ranking score pi \* L, descending with deterministic account-id tie-break; coherence-merged with the insurance-fund instance.                                                                                          |
| `adl_pnl_percent`          | $$\pi$$             | fraction              | \`\`                                      | ADL candidate's unrealized PnL as a fraction of position value; no corpus expression defines it — a gap for the closure gate.                                                                                              |
| `liquidation_penalty_owed` | $$\Lambda\_{owed}$$ | USDX                  | [(S.4)](/math-engine/settlement)          | Penalty owed on filled liquidation notional at the market's penalty rate.                                                                                                                                                  |
| `penalty_charged`          | $$\Lambda$$         | USDX                  | [(I.9)](/math-engine/insurance-fund)      | Penalty actually debited/credited: the owed amount capped at available collateral, so the pair cannot mint USDX.                                                                                                           |
| `fill_notional`            | $$V^{\star}$$       | USDX                  | [(S.1)](/math-engine/settlement)          | Notional of a single fill, size times price, exact decimal.                                                                                                                                                                |
| `net_exchange_revenue`     | $$V$$               | USDX                  | [(S.5)](/math-engine/settlement)          | Settlement-record closure: total taker fees minus total maker rebates.                                                                                                                                                     |
| `margin_ratio`             | $$\rho\_M$$         | dimensionless         | [(M.6)](/math-engine/margin-math)         | Equity over total notional; undefined at zero notional; diagnostic — gates nothing in the corpus.                                                                                                                          |
| `max_position_size`        | $$q\_{max}$$        | base units            | [(M.15)](/math-engine/margin-math)        | Largest lot-aligned position openable with given collateral at the market rate; sizing/display analysis.                                                                                                                   |
| `withdrawable_collateral`  | $$W\_{max}$$        | USDX                  | [(M.16)](/math-engine/margin-math)        | Full realized collateral for a flat, unreserved account, zero otherwise; the withdrawal guard's cap.                                                                                                                       |
| `best_bid`                 | $$P\_b$$            | USDX per base unit    | \`\`                                      | Highest resting bid — a structural readout of the order\_book coordinate; no corpus expression id.                                                                                                                         |
| `best_ask`                 | $$P\_a$$            | USDX per base unit    | \`\`                                      | Lowest resting ask — a structural readout of the order\_book coordinate; no corpus expression id.                                                                                                                          |
| `mid_price`                | $$P\_{mid}$$        | USDX per base unit    | [(B.4)](/math-engine/order-book)          | Midpoint of best bid and ask, snapshotted once at market-order submission for the slippage cap; undefined when either side is empty.                                                                                       |
| `slippage_span`            | $$\Delta\_{slip}$$  | USDX per base unit    | [(B.9)](/math-engine/order-book)          | Half-width of the admissible VWAP band anchored at the mid-price snapshot.                                                                                                                                                 |
| `available_qty`            | $$Q\_{avail}$$      | base units            | \`\`                                      | Pre-match opposing liquidity at prices satisfying the taker's limit; within-event readout of the book, prose-defined only.                                                                                                 |
| `taker_remaining`          | $$q\_t$$            | base units            | \`\`                                      | Taker's unfilled remainder during the matching walk; within-event intermediate, prose-defined only.                                                                                                                        |
| `running_notional`         | $$V\_k$$            | USDX                  | \`\`                                      | Cumulative notional of fills accepted so far in a market-order walk; within-event accumulator, prose-defined only.                                                                                                         |
| `running_filled`           | $$Q\_k$$            | base units            | \`\`                                      | Cumulative quantity of fills accepted so far in a market-order walk; within-event accumulator, prose-defined only.                                                                                                         |
| `walk_notional`            | $$V$$               | USDX                  | \`\`                                      | Total notional of a hypothetical price-time-priority walk for the VWAP preview; prose-defined only.                                                                                                                        |
| `vwap_estimate`            | $$\overline{P}$$    | USDX per base unit    | [(B.11)](/math-engine/order-book)         | Read-only market-order VWAP preview; undefined when liquidity cannot cover the request.                                                                                                                                    |
| `funding_integral`         | $$\varphi$$         | USDX                  | \`\`                                      | Per-position accumulated funding read by portfolio equity; identically zero under the composed settle-and-reset convention (phi = 0) — no accrual map exists in the corpus, by design.                                     |
| `total_notional`           | $$N$$               | USDX                  | \`\`                                      | Sum of size times mark over open positions; prose-defined only, the margin ratio's denominator.                                                                                                                            |
| `open_positions`           | $$n\_{pos}$$        | count                 | \`\`                                      | Count of the account's open positions — a structural readout of the position coordinates; no corpus expression id.                                                                                                         |

### A.4 The transition matrix

**Guards**

| Event                    | Guard                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Reads                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `deposit_withdrawal`     | deposit: x > 0 unguarded; withdrawal: 0 < x \le W\_{max} = C\_a , \mathbb{1}\[n\_{pos} = 0 \wedge n\_{resv} = 0] (margin-math.withdrawable\_collateral)                                                                                                                                                                                                                                                                                                                                                                  | `account_collateral`, `withdrawable_collateral`, `open_positions`, `reserved_margin`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `order_submission_fill`  | admission conjunction, nothing mutated on rejection: lot residue = 0 (order-book.lot\_alignment) \wedge tick residue = 0 \wedge P\_{lim} > 0 (order-book.tick\_alignment) \wedge band deviation \le b\_{band} (order-book.price\_band) \wedge FOK availability (order-book.fok\_availability) \wedge margin headroom \ge 0 (isolated: margin-math.order\_margin\_headroom; cross: margin-math.cross\_admission\_headroom); each market-order fill additionally gated by the running-VWAP band (order-book.running\_vwap) | `account_collateral`, `reserved_margin`, `signed_position_size`, `entry_price`, `order_book`, `trade_window`, `exchange_fee_account`, `mark_price`, `account_equity`, `available_margin`, `initial_margin`, `added_exposure`, `admission_added_margin`, `best_bid`, `best_ask`, `mid_price`, `slippage_span`, `available_qty`, `taker_remaining`, `running_notional`, `running_filled`, `fill_quantity`, `fill_price`, `closed_quantity`                                                                                                                                                                                                                                |
| `order_cancel_expiry`    | user cancel \vee IOC/FOK/market-remainder expiry \vee post-only cross rejection \vee slippage-cap breach (order-book.running\_vwap strict breach) \vee reduce-only rejection q\_{liq} > q (liquidation-engine.reduce\_only\_guard fails) \vee STP full reduction (order-book.stp\_decrement)                                                                                                                                                                                                                             | `order_book`, `reserved_margin`, `mid_price`, `slippage_span`, `liquidation_qty`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `funding_accrual`        | \Delta t > 0 \wedge u\_k > 0 (non-advancing or non-positive-anchor samples pass state through unchanged)                                                                                                                                                                                                                                                                                                                                                                                                                 | `accumulated_premium`, `interval_clock`, `oracle_anchor`, `mark_price`, `premium_index`, `trade_window`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `funding_settlement`     | funding interval boundary reached (schedule is configuration; the corpus contributes the T = 0 totality branch of funding-rate.funding\_rate)                                                                                                                                                                                                                                                                                                                                                                            | `accumulated_premium`, `interval_clock`, `funding_rate`, `funding_payment`, `mark_price`, `signed_position_size`, `account_collateral`, `funding_pool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `liquidation_cascade`    | E \le \sum\_i M\_i at the fresh mark, inclusive (liquidation-engine.liquidation\_trigger)                                                                                                                                                                                                                                                                                                                                                                                                                                | `account_collateral`, `signed_position_size`, `entry_price`, `order_book`, `trade_window`, `insurance_fund_balance`, `insurance_fund_total_absorbed`, `insurance_fund_total_received`, `exchange_fee_account`, `mark_price`, `unrealized_pnl`, `account_equity`, `maintenance_margin`, `position_loss`, `total_loss`, `cross_collateral_share`, `collateral_share_sum`, `bankruptcy_price`, `aligned_bankruptcy_price`, `safe_size`, `liquidation_qty`, `fill_price`, `fill_quantity`, `total_filled`, `spread_profit`, `realized_fill_pnl`, `residual_unfilled_pnl`, `bad_debt`, `absorbed_amount`, `liquidation_penalty_owed`, `penalty_charged`, `adl_settle_amount` |
| `adl_execution`          | D\_{adl} = \max(D - \Phi\_1, 0) > 0 (insurance-fund.adl\_settle\_amount) nested with \Phi' \le \kappa (insurance-fund.adl\_trigger); the one-sided divergence (armed with nothing to settle) is the open finding adl\_arming\_condition\_ambiguity                                                                                                                                                                                                                                                                       | `adl_settle_amount`, `insurance_fund_balance`, `adl_priority_score`, `adl_pnl_percent`, `signed_position_size`, `entry_price`, `account_collateral`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `oracle_print_accept`    | \neg stale (oracle.is\_stale = 0) \wedge \Delta\_{step} \le \theta (oracle.single\_step\_deviation) \wedge (\|H\| = N\_h \Rightarrow \Delta\_{path} \le \theta\sqrt{N\_h}) (oracle.path\_deviation vs oracle.path\_threshold)                                                                                                                                                                                                                                                                                            | `oracle_anchor`, `anchor_timestamp`, `oracle_guard_state`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `oracle_reanchor_step`   | stale (oracle.is\_stale = 1) \wedge print arrives \wedge confirmations after this step < \max(k, 2); active per-step bound is \theta\_r, widened to \theta\_e once the escalation trigger fires (oracle.reanchor\_step\_deviation, oracle.escalation\_trigger)                                                                                                                                                                                                                                                           | `oracle_guard_state`, `oracle_anchor`, `anchor_timestamp`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `oracle_reanchor_commit` | stale \wedge consecutive mutually-consistent confirmations \ge \max(k, 2) (oracle.reanchor\_step\_deviation within the active bound on the promoting print)                                                                                                                                                                                                                                                                                                                                                              | `oracle_guard_state`, `anchor_timestamp`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |

**Writes** — rows are state coordinates, columns are events; a cell cites the component equation defining that update; `·` means provably untouched.

| Coordinate                                   | `deposit_withdrawal` | `order_submission_fill`                                                                                  | `order_cancel_expiry` | `funding_accrual`                  | `funding_settlement`               | `liquidation_cascade`                                                                                                    | `adl_execution` | `oracle_print_accept` | `oracle_reanchor_step` | `oracle_reanchor_commit` |
| -------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------- | --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------- | --------------------- | ---------------------- | ------------------------ |
| `account_collateral` (per-account)           | ✓                    | [(T.4)](/math-engine/position-tracker) [(S.2)](/math-engine/settlement) [(S.3)](/math-engine/settlement) | ·                     | ·                                  | [(F.4)](/math-engine/funding-rate) | [(L.15)](/math-engine/liquidation-engine) [(L.16)](/math-engine/liquidation-engine) [(I.9)](/math-engine/insurance-fund) | ·               | ·                     | ·                      | ·                        |
| `reserved_margin` (per-account)              | ·                    | [(M.12)](/math-engine/margin-math)                                                                       | ✓                     | ·                                  | ·                                  | ·                                                                                                                        | ·               | ·                     | ·                      | ·                        |
| `signed_position_size` (per-position)        | ·                    | [(T.3)](/math-engine/position-tracker) [(T.5)](/math-engine/position-tracker) ✓                          | ·                     | ·                                  | ·                                  | [(T.3)](/math-engine/position-tracker)                                                                                   | ·               | ·                     | ·                      | ·                        |
| `entry_price` (per-position)                 | ·                    | [(T.1)](/math-engine/position-tracker)                                                                   | ·                     | ·                                  | ·                                  | [(T.1)](/math-engine/position-tracker)                                                                                   | ·               | ·                     | ·                      | ·                        |
| `order_book` (per-market)                    | ·                    | [(B.6)](/math-engine/order-book) [(B.8)](/math-engine/order-book)                                        | ✓                     | ·                                  | ·                                  | [(B.6)](/math-engine/order-book)                                                                                         | ·               | ·                     | ·                      | ·                        |
| `accumulated_premium` (per-market)           | ·                    | ·                                                                                                        | ·                     | [(F.2)](/math-engine/funding-rate) | ✓                                  | ·                                                                                                                        | ·               | ·                     | ·                      | ·                        |
| `interval_clock` (per-market)                | ·                    | ·                                                                                                        | ·                     | ✓                                  | ✓                                  | ·                                                                                                                        | ·               | ·                     | ·                      | ·                        |
| `trade_window` (per-market)                  | ·                    | [(B.7)](/math-engine/order-book)                                                                         | ·                     | ·                                  | ·                                  | [(B.7)](/math-engine/order-book)                                                                                         | ·               | ·                     | ·                      | ·                        |
| `insurance_fund_balance` (per-market)        | ·                    | ·                                                                                                        | ·                     | ·                                  | ·                                  | [(I.7)](/math-engine/insurance-fund) [(I.2)](/math-engine/insurance-fund) [(I.10)](/math-engine/insurance-fund)          | ·               | ·                     | ·                      | ·                        |
| `insurance_fund_total_absorbed` (per-market) | ·                    | ·                                                                                                        | ·                     | ·                                  | ·                                  | [(I.3)](/math-engine/insurance-fund)                                                                                     | ·               | ·                     | ·                      | ·                        |
| `insurance_fund_total_received` (per-market) | ·                    | ·                                                                                                        | ·                     | ·                                  | ·                                  | [(I.8)](/math-engine/insurance-fund) [(I.11)](/math-engine/insurance-fund)                                               | ·               | ·                     | ·                      | ·                        |
| `funding_pool` (global)                      | ·                    | ·                                                                                                        | ·                     | ·                                  | [(S.6)](/math-engine/settlement)   | ·                                                                                                                        | ·               | ·                     | ·                      | ·                        |
| `exchange_fee_account` (global)              | ·                    | [(S.2)](/math-engine/settlement) [(S.3)](/math-engine/settlement) [(S.7)](/math-engine/settlement)       | ·                     | ·                                  | ·                                  | [(S.2)](/math-engine/settlement)                                                                                         | ·               | ·                     | ·                      | ·                        |
| `oracle_anchor` (per-market)                 | ·                    | ·                                                                                                        | ·                     | ·                                  | ·                                  | ·                                                                                                                        | ·               | ✓                     | ·                      | ✓                        |
| `anchor_timestamp` (per-market)              | ·                    | ·                                                                                                        | ·                     | ·                                  | ·                                  | ·                                                                                                                        | ·               | ✓                     | ·                      | ✓                        |
| `oracle_guard_state` (per-market)            | ·                    | ·                                                                                                        | ·                     | ·                                  | ·                                  | ·                                                                                                                        | ·               | ✓                     | ✓                      | ✓                        |

### A.5 The invariant registry

Every invariant the corpus states — component and global — with its scope and how it is verified.

| #  | Invariant                                                                                                                                                                                                                                                                                                                                                                                 | Scope       | Where                                              | Verification          |
| -- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------- | --------------------- |
| 1  | The funding rate always lies in the cap band: $$f \in \[-c, +c]$$ for every reachable state ((F.3)).                                                                                                                                                                                                                                                                                      | component   | [Funding](/math-engine/funding-rate)               | component Judge       |
| 2  | (F.3) is total: it is defined for every state, including the empty interval, where $$T = 0$$ yields $$f = 0$$.                                                                                                                                                                                                                                                                            | component   | [Funding](/math-engine/funding-rate)               | component Judge       |
| 3  | Funding is zero-sum between matched sides: for equal size $$q$$, the long and short payments cancel exactly, $$\Pi^{f}*{long} + \Pi^{f}*{short} = 0$$ ((F.4)).                                                                                                                                                                                                                            | component   | [Funding](/math-engine/funding-rate)               | component Judge       |
| 4  | When the clamp is inactive, the sign of the rate matches the sign of the average premium: a perpetual trading persistently rich yields $$f > 0$$ and a perpetual trading cheap yields $$f < 0$$ ((F.3)).                                                                                                                                                                                  | component   | [Funding](/math-engine/funding-rate)               | component Judge       |
| 5  | The accumulator $$A$$ is a well-formed time-weighted sum: only samples with strictly advancing time and a strictly positive oracle price contribute ((F.2)).                                                                                                                                                                                                                              | component   | [Funding](/math-engine/funding-rate)               | component Judge       |
| 6  | Non-negativity with exact depletion: $$\Phi \ge 0$$ after every operation, and the balance is exactly zero — no dust, no overdraw — whenever an ADL instruction is emitted ((I.2)).                                                                                                                                                                                                       | component   | [The Insurance Fund](/math-engine/insurance-fund)  | component Judge       |
| 7  | Accounting closure: $$\Phi = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}}$$ at all times — including through the penalty receipt path, which required no correction on the fund side.                                                                                                                                                                                                  | component   | [The Insurance Fund](/math-engine/insurance-fund)  | component Judge       |
| 8  | Bad-debt conservation: $$D = \min(D, \Phi) + \max(D - \Phi, 0)$$ — every liquidation's debt is covered exactly once, split between fund absorption ((I.1)) and ADL settlement ((I.4)).                                                                                                                                                                                                    | component   | [The Insurance Fund](/math-engine/insurance-fund)  | component Judge       |
| 9  | Monotone ledgers: $$\Sigma\_{\mathrm{abs}}$$ and $$\Sigma\_{\mathrm{rec}}$$ never decrease, and liquidation processing leaves $$\Sigma\_{\mathrm{rec}}$$ untouched — ADL counterparty settlements live entirely outside the fund's books.                                                                                                                                                 | component   | [The Insurance Fund](/math-engine/insurance-fund)  | component Judge       |
| 10 | Credit-before-absorb sequencing: for a single liquidation carrying both spread profit $$g > 0$$ and bad debt $$D > 0$$, the spread credit lands first, so absorption evaluates against the post-credit balance — the fund absorbs $$\min(D, \Phi + g)$$ and ADL settles $$\max(D - \Phi - g, 0)$$ ((I.7), (I.1), (I.4)).                                                                  | component   | [The Insurance Fund](/math-engine/insurance-fund)  | component Judge       |
| 11 | No-mint penalty pair: the liquidatee debit and the fund credit are the same capped amount $$\Lambda = \min(\Lambda\_{\mathrm{owed}}, \max(C, 0))$$ ((I.9)) — the penalty transfers USDX, it never creates it.                                                                                                                                                                             | component   | [The Insurance Fund](/math-engine/insurance-fund)  | component Judge       |
| 12 | For a long position with positive backing collateral, the bankruptcy price (L.8) lies strictly below the entry price and at or below the liquidation-trigger price; for a short, strictly above and at or above, respectively.                                                                                                                                                            | component   | [Liquidation](/math-engine/liquidation-engine)     | component Judge       |
| 13 | The trigger threshold is inclusive: an account with $$E = \sum\_i M\_i$$ is liquidated ((L.4)).                                                                                                                                                                                                                                                                                           | component   | [Liquidation](/math-engine/liquidation-engine)     | component Judge       |
| 14 | Cross-collateral shares conserve the pool exactly: after (L.7), $$\sum\_i s\_i = C$$, so aggregate bad debt over a fully-unfilled liquidation equals $$\mathcal{L} - C$$, the true portfolio shortfall.                                                                                                                                                                                   | component   | [Liquidation](/math-engine/liquidation-engine)     | component Judge       |
| 15 | Tick alignment concedes at most one tick and never produces a non-positive limit: for $$\delta > 0$$, $$\|P - P^{\downarrow}\| < \delta$$ and $$P^{\downarrow} \ge \delta$$ (mirrored for (L.10)); for $$\delta \le 0$$ the price passes through unchanged.                                                                                                                               | component   | [Liquidation](/math-engine/liquidation-engine)     | component Judge       |
| 16 | In Partial mode, $$q\_{\text{liq}} \in (0, q]$$ holds if and only if the collateral share backing the position is non-negative. When $$C < 0$$, $$q\_{\text{safe}} < 0$$ and (L.12) yields $$q\_{\text{liq}} = q - q\_{\text{safe}} > q$$ — an oversized close that would flip the position if executed (finding liquidation\_close\_order\_exceeds\_position\_size, v0.0.9 audit, high). | component   | [Liquidation](/math-engine/liquidation-engine)     | component Judge       |
| 17 | A non-positive or missing mark never fabricates losses or prices an order: the trigger and the pool split evaluate such markets at entry (zero PnL contribution, (L.1)), and no liquidation order is generated for them.                                                                                                                                                                  | component   | [Liquidation](/math-engine/liquidation-engine)     | component Judge       |
| 18 | A fully-filled liquidation whose every fill prints at or above the raw bankruptcy price produces at most the sub-tick alignment concession $$q,\delta$$ of bad debt in (L.17) — exactly zero when the bankruptcy price is already tick-aligned.                                                                                                                                           | component   | [Liquidation](/math-engine/liquidation-engine)     | component Judge       |
| 19 | Maintenance never exceeds initial margin: for the same $$(q, P)$$, (M.4) $$\leq$$ (M.2), per position and in the portfolio sums.                                                                                                                                                                                                                                                          | component   | [Margining](/math-engine/margin-math)              | component Judge       |
| 20 | The effective rate never undercuts the market: $$r\_{eff} \geq r\_i$$ for every admissible leverage, so a selected leverage only ever reserves more margin ((M.1)).                                                                                                                                                                                                                       | component   | [Margining](/math-engine/margin-math)              | component Judge       |
| 21 | Added exposure is non-negative: (M.11) satisfies $$\Delta q \geq 0$$ for all $$(s\_0, o)$$, so the admission charge (M.12) can only hold the portfolio requirement flat or raise it — a reduce is never pre-credited margin.                                                                                                                                                              | component   | [Margining](/math-engine/margin-math)              | component Judge       |
| 22 | Required margin never rounds down: the computed (M.2) and (M.4) always satisfy $$M \geq q \cdot P \cdot r$$ exactly.                                                                                                                                                                                                                                                                      | component   | [Margining](/math-engine/margin-math)              | component Judge       |
| 23 | For $$C \geq 0$$, (M.15) returns a non-negative integer multiple of $$\ell$$ with $$q\_{max} \cdot P \cdot r\_i \leq C$$; the invariant is scoped to non-negative collateral.                                                                                                                                                                                                             | component   | [Margining](/math-engine/margin-math)              | component Judge       |
| 24 | Withdrawals move only realized collateral from flat accounts: an account with any open position or outstanding reservation withdraws nothing, and a flat account withdraws at most $$C$$ ((M.16)); unrealized PnL is never withdrawable.                                                                                                                                                  | component   | [Margining](/math-engine/margin-math)              | component Judge       |
| 25 | Concurrent orders cannot double-spend headroom: the admission comparison in (M.14) includes all outstanding reservations $$M\_{resv}$$, and the check and the reservation write are atomic.                                                                                                                                                                                               | component   | [Margining](/math-engine/margin-math)              | component Judge       |
| 26 | The mark price always lies between the oracle anchor and the trade reference: $$\min(P\_{oracle}, P\_{trade}) \le P\_{mark} \le \max(P\_{oracle}, P\_{trade})$$, and $$P\_{mark} = P\_{oracle}$$ exactly when no trade reference exists.                                                                                                                                                  | component   | [The Oracle](/math-engine/oracle)                  | component Judge       |
| 27 | While a re-anchor is pending, the trusted anchor and its timestamp never move: $$P\_{oracle}$$ and $$t\_{last}$$ are unchanged by every pending print, so an unconfirmed candidate can never reach the mark.                                                                                                                                                                              | component   | [The Oracle](/math-engine/oracle)                  | component Judge       |
| 28 | On a fresh anchor, every trusted print satisfies $$\Delta\_{step} \le \theta$$ ((O.1)), and once the history window is full, $$\Delta\_{path} \le \theta\sqrt{N\_h}$$ ((O.3)) as well — the anchor moves by at most a factor $$1+\theta$$ per print and its ten-print path is square-root bounded.                                                                                        | component   | [The Oracle](/math-engine/oracle)                  | component Judge       |
| 29 | A large move off a stale anchor is never trusted from a single print: promotion requires at least $$\max(k, 2) \ge 2$$ consecutive prints each within the active step bound of the running candidate, even when escalated.                                                                                                                                                                | component   | [The Oracle](/math-engine/oracle)                  | component Judge       |
| 30 | A single trade — including a wash or self-trade fired immediately before a liquidation check — cannot move the mark by more than the volume-weighted median allows: shifting $$P\_{trade}$$ requires manipulated prices to carry strictly more than half the window's total traded size.                                                                                                  | component   | [The Oracle](/math-engine/oracle)                  | component Judge       |
| 31 | The book is never crossed: whenever both sides are non-empty, $$P\_b < P\_a$$.                                                                                                                                                                                                                                                                                                            | component   | [The Order Book](/math-engine/order-book)          | component Judge       |
| 32 | No order overfills: $$q\_f \le q$$ throughout an order's life, so remaining quantity $$q - q\_f$$ never goes negative.                                                                                                                                                                                                                                                                    | component   | [The Order Book](/math-engine/order-book)          | component Judge       |
| 33 | Every fill respects the taker's limit: a buy fills at $$P^{\star} \le P\_{lim}$$ and a sell at $$P^{\star} \ge P\_{lim}$$.                                                                                                                                                                                                                                                                | component   | [The Order Book](/math-engine/order-book)          | component Judge       |
| 34 | Every resting order has strictly positive remaining quantity, $$q - q\_f > 0$$.                                                                                                                                                                                                                                                                                                           | component   | [The Order Book](/math-engine/order-book)          | component Judge       |
| 35 | The order index and the book agree exactly: the index holds precisely the ids of resting orders, each mapped to its true side and price.                                                                                                                                                                                                                                                  | component   | [The Order Book](/math-engine/order-book)          | component Judge       |
| 36 | The slippage cap is monotone: for a fixed book and taker, a larger cap $$\beta$$ fills at least as much quantity as a smaller one.                                                                                                                                                                                                                                                        | component   | [The Order Book](/math-engine/order-book)          | component Judge       |
| 37 | Every stored position has strictly positive size: $$q > 0$$.                                                                                                                                                                                                                                                                                                                              | component   | [Position Tracking](/math-engine/position-tracker) | component Judge       |
| 38 | The entry price is a convex combination of the fill prices that built the current side, so $$P\_e > 0$$ and $$q,P\_e$$ equals the total notional paid; the aggregate entry is independent of the order of same-side fills.                                                                                                                                                                | component   | [Position Tracking](/math-engine/position-tracker) | component Judge       |
| 39 | A partial close changes size but never entry price, and the settled amount is exactly (T.4) at the closing fill's price, not the mark.                                                                                                                                                                                                                                                    | component   | [Position Tracking](/math-engine/position-tracker) | component Judge       |
| 40 | Bankruptcy is at least as adverse as liquidation: $$\sigma,(P\_{liq} - P\_{bkr}) \ge 0$$ for any position that is not over-collateralized ($$C \le P\_e,q$$, $$0 \le r\_m < 1$$).                                                                                                                                                                                                         | component   | [Position Tracking](/math-engine/position-tracker) | component Judge       |
| 41 | P\&L channels are disjoint: a funding payment changes only the funding accumulator, fees change only the fee channel, and neither moves the entry price, size, or realized P\&L.                                                                                                                                                                                                          | component   | [Position Tracking](/math-engine/position-tracker) | component Judge       |
| 42 | Fee accounting closure: within any settlement record, $$V = F - R$$ ((S.5)) holds exactly — every unit collected as a taker fee is either paid out as a maker rebate or retained as revenue, and money is neither created nor destroyed.                                                                                                                                                  | component   | [Settlement](/math-engine/settlement)              | component Judge       |
| 43 | Non-negativity of all transfer amounts: $$\text{fee}\_t \ge 0$$ ((S.2)), $$\text{rebate}\_m \ge 0$$ ((S.3)), and $$\text{penalty} \ge 0$$ ((S.4)), and every emitted funding transfer carries a strictly positive amount $$\|x\| > 0$$ ((S.6)).                                                                                                                                           | component   | [Settlement](/math-engine/settlement)              | component Judge       |
| 44 | Liquidation-penalty destination: every liquidation penalty transfer originates at the taker (the liquidated account) and terminates at the insurance fund — it is never routed to the Exchange fee account and never enters $$F$$, $$R$$, or $$V$$.                                                                                                                                       | component   | [Settlement](/math-engine/settlement)              | component Judge       |
| 45 | Merge conservation: for any batch, each merged accumulator equals the sum of that accumulator over the constituents ((S.7)), the merged transfer count equals the sum of constituent transfer counts, and the merged period is $$\[\min\_j \text{start}\_j, \max\_j \text{end}\_j]$$.                                                                                                     | component   | [Settlement](/math-engine/settlement)              | component Judge       |
| 46 | $$\sum\_{a} \Pi^{f}*{a} ;=; f, m\_k \sum*{a} s\_{a,k} ;=; 0 \qquad\Longrightarrow\qquad C\_{pool}' = C\_{pool}$$ — Aggregate funding zero-sum across matched open interest                                                                                                                                                                                                                | open-loop   | global model                                       | composition Judge     |
| 47 | $$\sum\_i s\_i = C, \qquad D = \min(D, \Phi\_1) + \max(D - \Phi\_1,\ 0), \qquad \Delta C\_a\big\|*{\Lambda} = -\Lambda = -\Delta\Phi\big\|*{\Lambda}, \qquad \Phi = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}}$$ — Collateral conservation through the liquidation cascade                                                                                                           | open-loop   | global model                                       | composition Judge     |
| 48 | $$r\_m < r\_i ;\Longrightarrow; \big{, S : E \le M\_{maint}^{pf} ,\big} \subsetneq \big{, S : E \le M\_{init}^{pf} ,\big}$$ — Margin monotonicity (maintenance strictly inside initial)                                                                                                                                                                                                   | open-loop   | global model                                       | simulator, 512 trials |
| 49 | $$\lvert \Pi^{f}\_{a} \rvert ;\le; c, q\_a, m\_k \qquad \text{per position, per funding interval}$$ — Bounded funding transfer per interval                                                                                                                                                                                                                                               | open-loop   | global model                                       | simulator, 512 trials |
| 50 | $$\big\lvert E(S, u') - E(S, u) \big\rvert ;\le; w, \theta, P\_{oracle} \sum\_i q\_i \qquad \text{(fresh-anchor accept branch only)}$$ — Bounded equity shock per fresh-anchor tick                                                                                                                                                                                                       | closed-loop | global model                                       | composition Judge     |
| 51 | $$\mathrm{stale} ;\Longrightarrow; (P\_{oracle}',, t\_{last}') = (P\_{oracle},, t\_{last}) \quad\text{and}\quad \lvert \Delta m\_k \rvert \le (1 - w), \lvert \Delta P\_{trade} \rvert$$ — Staleness freezes the anchor leg only                                                                                                                                                          | closed-loop | global model                                       | composition Judge     |
| 52 | $$\Delta C\_{fee} ;=; F - R ;=; V, \qquad \Delta C\_{fee} + \textstyle\sum\_a \Delta C\_a \big\|\_{\text{fees}} = 0$$ — Fee closure across the fill path                                                                                                                                                                                                                                  | open-loop   | global model                                       | composition Judge     |

## References

* The oracle-endogenous closed loop: [closed-loop](/math-engine/closed-loop)
* Component model: [funding-rate](/math-engine/funding-rate)
* Component model: [insurance-fund](/math-engine/insurance-fund)
* Component model: [liquidation-engine](/math-engine/liquidation-engine)
* Component model: [margin-math](/math-engine/margin-math)
* Component model: [oracle](/math-engine/oracle)
* Component model: [order-book](/math-engine/order-book)
* Component model: [position-tracker](/math-engine/position-tracker)
* Component model: [settlement](/math-engine/settlement)


# System

The oracle's guarded mark price is the system's central clock: it simultaneously drives the funding premium, revalues every position's unrealized PnL, sets maintenance requirements, and collars order prices, while executed fills feed back into the trade window that shapes the mark itself. Account health composes as fills → entry price → unrealized PnL → equity → margin ratios and admission headroom, and when equity crosses total maintenance margin the liquidation cascade fires: collateral is apportioned, bankruptcy-priced close orders execute, and the settlement residue splits into spread profit and penalties credited to the insurance fund versus bad debt drawn from it, with ADL as the terminal backstop. Funding closes the loop as a periodic value transfer that debits or credits equity through the settlement layer, coupling the price-formation and account-health subsystems continuously rather than only at liquidation.

84 expressions, 86 relationships (41 cross-primitive), across 6 clusters.

![The full relationship graph: every tagged expression grouped by primitive (order-book, position-tracker, margin-math, oracle, funding-rate, liquidation-engine, insurance-fund), with edges showing which expressions feed, bound, or trigger which others.](/files/o7dVQK3F6HENJ1587opO)

*The interactive version of this graph — pan, zoom, and click through to each expression — lives on the engine's own site; this is a static capture of it.*

## Clusters

### Price formation & oracle defense

Deviation guards, staleness, and re-anchor escalation defend the trusted anchor that blends with the trade median into the mark price every downstream primitive consumes.

`oracle.single_step_deviation`, `oracle.path_deviation`, `oracle.path_threshold`, `oracle.is_stale`, `oracle.reanchor_step_deviation`, `oracle.escalation_trigger`, `oracle.trade_ref`, `oracle.mark_price`

### Order admission & matching

Margin headroom gates, alignment and band checks, and the price-time matching loop that decides which orders enter the book and at what prices and quantities they fill.

`margin-math.effective_initial_margin_rate`, `margin-math.initial_margin_required`, `margin-math.initial_margin_required_effective`, `margin-math.added_exposure`, `margin-math.admission_added_margin`, `margin-math.order_margin_headroom`, `margin-math.cross_admission_headroom`, `margin-math.max_position_size`, `order-book.lot_alignment`, `order-book.tick_alignment`, `order-book.price_band`, `order-book.mid_price`, `order-book.fok_availability`, `order-book.fill_quantity`, `order-book.fill_price`, `order-book.stp_decrement`, `order-book.slippage_span`, `order-book.running_vwap`, `order-book.vwap_estimate`

### Account health

The position lifecycle (entry, PnL realization, flips) rolled up through equity, margin ratios, and the displayed liquidation/bankruptcy prices that describe how close an account is to the cascade.

`margin-math.equity`, `margin-math.margin_ratio`, `margin-math.available_margin`, `margin-math.portfolio_equity`, `margin-math.portfolio_initial_margin`, `margin-math.portfolio_maintenance_margin`, `margin-math.maintenance_margin_required`, `margin-math.withdrawable_collateral`, `position-tracker.vwap_entry`, `position-tracker.unrealized_pnl`, `position-tracker.realized_pnl`, `position-tracker.closed_quantity`, `position-tracker.flip_size`, `position-tracker.liquidation_price`, `position-tracker.bankruptcy_price`

### Liquidation cascade

From the equity-versus-maintenance trigger through collateral apportionment, bankruptcy-priced close orders, and fills-aware settlement into spread profit, bad debt, and ADL ranking.

`liquidation-engine.fresh_unrealized_pnl`, `liquidation-engine.account_equity`, `liquidation-engine.maintenance_margin`, `liquidation-engine.liquidation_trigger`, `liquidation-engine.isolated_margin_fallback`, `liquidation-engine.cross_collateral_share`, `liquidation-engine.cross_remainder_fold`, `liquidation-engine.bankruptcy_price`, `liquidation-engine.aligned_price_sell`, `liquidation-engine.aligned_price_buy`, `liquidation-engine.safe_size`, `liquidation-engine.partial_liquidation_qty`, `liquidation-engine.reduce_only_guard`, `liquidation-engine.spread_profit`, `liquidation-engine.realized_fill_pnl`, `liquidation-engine.residual_unfilled_pnl`, `liquidation-engine.bad_debt`, `liquidation-engine.adl_priority_score`

### Loss absorption backstop

The insurance fund's balance-capped debt absorption, its spread-profit and penalty income with lockstep lifetime ledgers, and the ADL threshold and settlement path when the fund runs dry.

`insurance-fund.absorbed_amount`, `insurance-fund.post_liquidation_balance`, `insurance-fund.total_absorbed_update`, `insurance-fund.adl_settle_amount`, `insurance-fund.adl_trigger`, `insurance-fund.adl_priority_score`, `insurance-fund.spread_profit_balance`, `insurance-fund.total_received_update`, `insurance-fund.penalty_charged`, `insurance-fund.penalty_credit_balance`, `insurance-fund.penalty_received_update`

### Funding & settlement flows

Periodic value transfers — the time-weighted funding rate settling as signed payments, and per-fill fees, rebates, and penalties accumulating into exchange revenue and fund income.

`funding-rate.premium_index`, `funding-rate.sample_contribution`, `funding-rate.funding_rate`, `funding-rate.funding_payment`, `settlement.funding_transfer`, `settlement.fill_notional`, `settlement.taker_fee`, `settlement.maker_rebate`, `settlement.liquidation_penalty`, `settlement.net_exchange_revenue`, `settlement.merge_totals`, `position-tracker.taker_fee`, `position-tracker.maker_rebate`

## Relationships

| From                                          | Kind     | To                                           | How                                                                                                                                                                                                                                                          |
| --------------------------------------------- | -------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `order-book.fill_price`                       | feeds    | `oracle.trade_ref`                           | Executed fill prices populate the five-trade window whose volume-weighted median becomes the trade reference.                                                                                                                                                |
| `oracle.trade_ref`                            | feeds    | `oracle.mark_price`                          | The volume-weighted trade median is the trade side of the convex mark-price blend.                                                                                                                                                                           |
| `oracle.mark_price`                           | feeds    | `funding-rate.premium_index`                 | The mark price is the numerator deviation term measured against the oracle anchor in every premium sample.                                                                                                                                                   |
| `oracle.mark_price`                           | feeds    | `position-tracker.unrealized_pnl`            | Every mark update revalues each open position's unrealized PnL against its entry price.                                                                                                                                                                      |
| `oracle.mark_price`                           | feeds    | `liquidation-engine.fresh_unrealized_pnl`    | The liquidation engine recomputes per-position PnL from the fresh mark rather than trusting cached values.                                                                                                                                                   |
| `oracle.mark_price`                           | feeds    | `liquidation-engine.maintenance_margin`      | Maintenance margin is notional at the fresh mark scaled by the maintenance rate.                                                                                                                                                                             |
| `oracle.mark_price`                           | feeds    | `order-book.price_band`                      | The mark anchors the basis-point collar that admissible limit prices are measured against.                                                                                                                                                                   |
| `funding-rate.premium_index`                  | feeds    | `funding-rate.sample_contribution`           | Each sample's premium is time-weighted and added to the interval accumulator.                                                                                                                                                                                |
| `funding-rate.sample_contribution`            | feeds    | `funding-rate.funding_rate`                  | The accumulated time-weighted premium is the numerator of the clamped average funding rate.                                                                                                                                                                  |
| `funding-rate.funding_rate`                   | feeds    | `funding-rate.funding_payment`               | The settled rate scales each position's mark-price notional into a signed cash payment.                                                                                                                                                                      |
| `funding-rate.funding_payment`                | feeds    | `settlement.funding_transfer`                | The signed funding payment is the amount routed through the funding pool, its sign selecting the transfer direction.                                                                                                                                         |
| `order-book.fill_price`                       | feeds    | `position-tracker.vwap_entry`                | Same-side fills fold their price into the size-weighted average entry price.                                                                                                                                                                                 |
| `position-tracker.unrealized_pnl`             | feeds    | `margin-math.equity`                         | Total unrealized PnL over open positions is the mark-to-market component of account equity.                                                                                                                                                                  |
| `margin-math.equity`                          | feeds    | `margin-math.margin_ratio`                   | Equity is the numerator of the margin ratio over total notional.                                                                                                                                                                                             |
| `margin-math.equity`                          | feeds    | `margin-math.available_margin`               | Available margin is equity less the initial margin already held against open positions.                                                                                                                                                                      |
| `margin-math.available_margin`                | feeds    | `margin-math.order_margin_headroom`          | Isolated-path admission headroom is available margin minus the order's effective-rate requirement.                                                                                                                                                           |
| `margin-math.order_margin_headroom`           | bounds   | `order-book.fill_quantity`                   | An order reaches the matching loop only if its margin headroom is non-negative, constraining which quantities can ever fill.                                                                                                                                 |
| `liquidation-engine.fresh_unrealized_pnl`     | feeds    | `liquidation-engine.account_equity`          | Fresh per-position PnL sums with collateral into the equity the trigger evaluates.                                                                                                                                                                           |
| `liquidation-engine.account_equity`           | feeds    | `liquidation-engine.liquidation_trigger`     | Equity is the left side of the inclusive trigger comparison.                                                                                                                                                                                                 |
| `liquidation-engine.maintenance_margin`       | bounds   | `liquidation-engine.liquidation_trigger`     | Total maintenance margin is the inclusive floor below which equity fires the liquidation.                                                                                                                                                                    |
| `liquidation-engine.liquidation_trigger`      | triggers | `liquidation-engine.cross_collateral_share`  | Only a fired trigger causes the shared cross pool to be split loss-proportionally across liquidating markets.                                                                                                                                                |
| `liquidation-engine.cross_collateral_share`   | feeds    | `liquidation-engine.bankruptcy_price`        | Each market's final collateral share (after the remainder fold) is the C displacing entry price into that position's bankruptcy price.                                                                                                                       |
| `position-tracker.vwap_entry`                 | feeds    | `liquidation-engine.bankruptcy_price`        | The volume-weighted entry price is the anchor from which the collateral share per unit displaces the bankruptcy price.                                                                                                                                       |
| `liquidation-engine.bankruptcy_price`         | feeds    | `liquidation-engine.spread_profit`           | The tick-aligned bankruptcy price is the reference each fill's price improvement is measured against.                                                                                                                                                        |
| `order-book.fill_price`                       | feeds    | `liquidation-engine.realized_fill_pnl`       | Liquidation close orders settle each fill's PnL at the maker price the book actually printed.                                                                                                                                                                |
| `liquidation-engine.realized_fill_pnl`        | feeds    | `liquidation-engine.bad_debt`                | Fills-aware realized PnL enters the post-liquidation equity whose negative part is bad debt.                                                                                                                                                                 |
| `liquidation-engine.spread_profit`            | feeds    | `liquidation-engine.bad_debt`                | Spread profit is subtracted from retained collateral in the shortfall computation.                                                                                                                                                                           |
| `liquidation-engine.spread_profit`            | feeds    | `insurance-fund.spread_profit_balance`       | Positive liquidation spread profit is credited to the insurance fund balance before absorption is evaluated.                                                                                                                                                 |
| `liquidation-engine.bad_debt`                 | feeds    | `insurance-fund.absorbed_amount`             | The liquidation's non-negative shortfall is the debt the fund attempts to absorb, capped by its balance.                                                                                                                                                     |
| `insurance-fund.absorbed_amount`              | feeds    | `insurance-fund.post_liquidation_balance`    | The new fund balance is the old balance less the absorbed amount, floored at zero.                                                                                                                                                                           |
| `insurance-fund.post_liquidation_balance`     | feeds    | `insurance-fund.adl_trigger`                 | The post-absorption balance is what the inclusive per-market ADL threshold predicate evaluates.                                                                                                                                                              |
| `liquidation-engine.bad_debt`                 | feeds    | `insurance-fund.adl_settle_amount`           | Bad debt exceeding the fund balance emits an ADL instruction for exactly the excess.                                                                                                                                                                         |
| `order-book.fill_price`                       | feeds    | `settlement.fill_notional`                   | Every fill's maker price times quantity is the notional on which all settlement fees and penalties are computed.                                                                                                                                             |
| `settlement.fill_notional`                    | feeds    | `settlement.liquidation_penalty`             | The liquidation penalty is the fill notional scaled by the penalty rate on liquidation fills only.                                                                                                                                                           |
| `settlement.liquidation_penalty`              | feeds    | `insurance-fund.penalty_charged`             | The settlement-computed penalty is the owed amount the fund charges, capped at the liquidatee's available collateral.                                                                                                                                        |
| `funding-rate.funding_payment`                | feeds    | `margin-math.equity`                         | Funding settles atomically into the collateral balance C (settle-and-reset), the first term of equity; the accrual integral is identically zero at every observable state, so the live channel is C, not the phi term.                                       |
| `order-book.fill_quantity`                    | feeds    | `position-tracker.closed_quantity`           | The matched fill quantity is the amount tested against existing position size to determine closed quantity.                                                                                                                                                  |
| `margin-math.initial_margin_required`         | feeds    | `margin-math.available_margin`               | Per-position initial margin (or stamped allocated margin) is the hold subtracted from equity.                                                                                                                                                                |
| `liquidation-engine.cross_collateral_share`   | feeds    | `liquidation-engine.cross_remainder_fold`    | The pure proportional shares are corrected by folding the rounding remainder into the largest-loss share.                                                                                                                                                    |
| `liquidation-engine.cross_remainder_fold`     | feeds    | `liquidation-engine.bankruptcy_price`        | Each market's folded collateral share is the C that sets its bankruptcy price.                                                                                                                                                                               |
| `liquidation-engine.liquidation_trigger`      | triggers | `liquidation-engine.partial_liquidation_qty` | Close-order sizing runs only for accounts the trigger selected.                                                                                                                                                                                              |
| `insurance-fund.adl_settle_amount`            | triggers | `insurance-fund.adl_priority_score`          | A strictly positive unabsorbed amount emits an ADL instruction whose counterparties are ranked by descending score.                                                                                                                                          |
| `liquidation-engine.partial_liquidation_qty`  | feeds    | `order-book.fill_quantity`                   | The cascade's close orders execute through the book: liquidation sizing becomes matched fill quantity, closing the loop by which liquidation fills re-enter the trade window and hence the mark.                                                             |
| `order-book.fill_price`                       | feeds    | `liquidation-engine.spread_profit`           | P\*\_j in the spread-profit sum is the book's executed fill price; the bankruptcy price only bounds it.                                                                                                                                                      |
| `position-tracker.realized_pnl`               | feeds    | `margin-math.equity`                         | Realized PnL settles into collateral C on the fill (C' = C + sigma(P\* - P\_e)q\_c), the first term of equity — the realized channel alongside the unrealized one.                                                                                           |
| `settlement.taker_fee`                        | feeds    | `margin-math.equity`                         | Taker fees debit the collateral balance C at fill time — the same collateral channel into equity as realized PnL.                                                                                                                                            |
| `settlement.maker_rebate`                     | feeds    | `margin-math.equity`                         | Maker rebates credit the collateral balance C at fill time — the same collateral channel into equity as realized PnL.                                                                                                                                        |
| `oracle.mark_price`                           | feeds    | `margin-math.portfolio_maintenance_margin`   | Portfolio maintenance margin is valued at the mark (sum q\_i \* m\_i \* r\_mm); wires the margin-math cluster to price formation.                                                                                                                            |
| `oracle.mark_price`                           | feeds    | `margin-math.portfolio_initial_margin`       | Portfolio initial margin is valued at the mark (sum q\_i \* m\_i \* r\_im).                                                                                                                                                                                  |
| `oracle.mark_price`                           | feeds    | `liquidation-engine.safe_size`               | Close-order sizing consumes the mark directly (C / (1.5 \* m \* r\_i)) — the mark's third entry point into the cascade.                                                                                                                                      |
| `oracle.mark_price`                           | feeds    | `liquidation-engine.residual_unfilled_pnl`   | The unfilled remainder is valued at the mark on its way into bad\_debt — the shortfall computation's mark dependency.                                                                                                                                        |
| `settlement.liquidation_penalty`              | feeds    | `margin-math.equity`                         | The cascade debits the penalty from the account's residual collateral (C\_a' = ... - sum Lambda\_i) — the account-side leg of the penalty transfer. The fund-side leg is penalty\_charged -> penalty\_credit\_balance.                                       |
| `insurance-fund.adl_trigger`                  | triggers | `insurance-fund.adl_priority_score`          | The threshold arm-path 1\[Phi' <= kappa]. Settle-amount firing implies threshold firing (D\_adl > 0 forces Phi' = 0 <= kappa); the divergence is one-sided — the threshold can fire with D\_adl = 0 (open finding: adl\_arming\_is\_nested\_not\_ambiguous). |
| `oracle.mark_price`                           | feeds    | `funding-rate.funding_payment`               | The payment sigma*S*P\_mark\*f consumes the mark directly as notional valuation — a dependency distinct from the rate chain.                                                                                                                                 |
| `liquidation-engine.safe_size`                | feeds    | `liquidation-engine.partial_liquidation_qty` | partial\_liquidation\_qty embeds q\_safe verbatim (size - q\_safe; full mode when q\_safe >= size) — the sizing sub-chain.                                                                                                                                   |
| `liquidation-engine.isolated_margin_fallback` | feeds    | `liquidation-engine.bankruptcy_price`        | For isolated positions the stamped cushion (or q*P\_e*r\_i fallback) is the C in p\_b = P\_e - d\*C/q — the isolated collateral path into bankruptcy pricing.                                                                                                |
| `liquidation-engine.residual_unfilled_pnl`    | feeds    | `liquidation-engine.bad_debt`                | bad\_debt consumes the residual unfilled PnL leg directly — the second PnL leg of the shortfall computation.                                                                                                                                                 |
| `position-tracker.unrealized_pnl`             | feeds    | `insurance-fund.adl_priority_score`          | pnl\_percent in the ADL ranking derives from position unrealized PnL — the ranking's inbound dependency on the position layer.                                                                                                                               |
| `oracle.is_stale`                             | triggers | `oracle.reanchor_step_deviation`             | The re-anchor protocol runs only off a stale anchor; is\_stale routes prints into candidate confirmation — the control edge between the oracle's two regimes.                                                                                                |
| `order-book.mid_price`                        | feeds    | `order-book.slippage_span`                   | slippage\_span = mid\_price \* beta / 10^4 — the slippage band is priced off the mid.                                                                                                                                                                        |
| `order-book.slippage_span`                    | bounds   | `order-book.running_vwap`                    | The running VWAP of a market-order walk is bounded by the slippage band around the mid.                                                                                                                                                                      |
| `order-book.fill_quantity`                    | feeds    | `position-tracker.vwap_entry`                | fill\_quantity is the blend weight of the VWAP entry update (q*Pe + q\_f*P\*)/(q + q\_f) — the quantity leg alongside the price leg.                                                                                                                         |
| `order-book.fill_price`                       | feeds    | `position-tracker.realized_pnl`              | Realized PnL is valued at the executed fill price P\*.                                                                                                                                                                                                       |
| `position-tracker.closed_quantity`            | feeds    | `position-tracker.realized_pnl`              | closed\_qty = min(q, q\_f) is a declared input of realized PnL — the reduce-path quantity.                                                                                                                                                                   |
| `oracle.mark_price`                           | feeds    | `margin-math.margin_ratio`                   | margin\_ratio's denominator total\_notional = sum q\_i \* m\_i is valued at the mark.                                                                                                                                                                        |
| `liquidation-engine.bankruptcy_price`         | feeds    | `liquidation-engine.aligned_price_sell`      | Close orders are tick-aligned toward executability from the bankruptcy-derived close price (sell side).                                                                                                                                                      |
| `liquidation-engine.bankruptcy_price`         | feeds    | `liquidation-engine.aligned_price_buy`       | Close orders are tick-aligned toward executability from the bankruptcy-derived close price (buy side).                                                                                                                                                       |
| `order-book.fill_quantity`                    | feeds    | `liquidation-engine.spread_profit`           | The per-fill spread-profit sum consumes fill quantities from the book's matching output.                                                                                                                                                                     |
| `order-book.fill_quantity`                    | feeds    | `liquidation-engine.realized_fill_pnl`       | Realized fill PnL consumes fill quantities from the matching output.                                                                                                                                                                                         |
| `order-book.fill_quantity`                    | feeds    | `liquidation-engine.residual_unfilled_pnl`   | The residual leg consumes total filled quantity to size the unfilled remainder.                                                                                                                                                                              |
| `position-tracker.vwap_entry`                 | feeds    | `position-tracker.unrealized_pnl`            | entry\_price is maintained by the VWAP entry update and read by unrealized PnL — the fills -> entry -> PnL chain.                                                                                                                                            |
| `position-tracker.vwap_entry`                 | feeds    | `liquidation-engine.fresh_unrealized_pnl`    | The liquidation engine recomputes PnL from the entry price that only vwap\_entry maintains.                                                                                                                                                                  |
| `insurance-fund.spread_profit_balance`        | feeds    | `insurance-fund.absorbed_amount`             | Absorption evaluates against the post-credit balance Phi\_1 = Phi + g (credit-before-absorb, code-verified 2026-07-12).                                                                                                                                      |
| `insurance-fund.penalty_charged`              | feeds    | `insurance-fund.penalty_credit_balance`      | Lambda is the penalty input of the fund-side credit — the no-mint pair's fund leg.                                                                                                                                                                           |
| `margin-math.equity`                          | feeds    | `margin-math.cross_admission_headroom`       | Equity is the first input of the cross admission check E - (M\_used + M\_resv + M\_add).                                                                                                                                                                     |
| `margin-math.admission_added_margin`          | feeds    | `margin-math.cross_admission_headroom`       | Exposure netting enters admission here: added initial margin is a declared input of the cross headroom check.                                                                                                                                                |
| `margin-math.added_exposure`                  | feeds    | `margin-math.admission_added_margin`         | Netted added exposure feeds the added-margin computation on the cross path (the isolated path charges gross size — open finding isolated\_headroom\_charges\_gross\_size).                                                                                   |
| `order-book.fill_quantity`                    | feeds    | `settlement.fill_notional`                   | fill\_notional = q \* P — the quantity leg alongside the price leg.                                                                                                                                                                                          |
| `settlement.fill_notional`                    | feeds    | `settlement.taker_fee`                       | Fees are notional \* bps; wires the exchange-revenue chain to the fill.                                                                                                                                                                                      |
| `liquidation-engine.partial_liquidation_qty`  | feeds    | `liquidation-engine.reduce_only_guard`       | The computed close quantity is the guard's tested input — the negative-share stall path (finding liquidation\_close\_order\_exceeds\_position\_size).                                                                                                        |
| `insurance-fund.penalty_credit_balance`       | feeds    | `insurance-fund.absorbed_amount`             | Absorption evaluates against the post-credit balance Phi\_1 = Phi + Lambda + g — the penalty leg of credit-before-absorb (code-verified 2026-07-12, per\_market.rs:552-574).                                                                                 |
| `position-tracker.vwap_entry`                 | feeds    | `position-tracker.liquidation_price`         | The displayed liquidation price consumes the entry price that only vwap\_entry maintains.                                                                                                                                                                    |
| `position-tracker.vwap_entry`                 | feeds    | `position-tracker.bankruptcy_price`          | The display twin of the engine's bankruptcy price likewise consumes the VWAP-maintained entry price.                                                                                                                                                         |
| `oracle.single_step_deviation`                | bounds   | `oracle.mark_price`                          | The accept guard bounds anchor-leg motion of the mark to theta per tick — the channel the bounded-equity-shock invariant composes.                                                                                                                           |
| `liquidation-engine.aligned_price_sell`       | bounds   | `order-book.fill_price`                      | The tick-aligned close price is the cascade order's limit — it bounds which maker prices liquidation fills can print at (sell side).                                                                                                                         |
| `liquidation-engine.aligned_price_buy`        | bounds   | `order-book.fill_price`                      | Buy-side twin: the aligned close price bounds liquidation fill prices, closing the cascade's pricing loop through the book.                                                                                                                                  |

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.


# Closed Loop

The Nexus Exchange is one dynamical system. This document formalizes it as a state-space model in three staged parts, from a single corpus of verified component mathematics. Part I is the heart: the Exchange **engine** alone, a self-contained transition system $$S' = f(S, u)$$ in which the oracle price $$u$$ is simply *given* — every economic fact of the venue (matching, position lifecycle, funding, margining, liquidation, loss absorption) is an event map acting on one state vector. Part II summarizes the **input process**: $$u$$ is not raw but the output of a guarded process $$u' = g(u, \text{prints})$$, detailed in the oracle component document. Part III is the **union**: the closed loop obtained by composing the two, whose invariants — bounded equity shock per accepted tick, the conditional freeze under staleness — exist only because the guarded input and the engine response interlock.

Notation throughout matches the component corpus: $$C\_a$$ collateral, $$s$$ signed position size with $$\sigma = \operatorname{sign}(s)$$ and $$q = |s|$$, $$P\_e$$ entry price, $$m\_k$$ mark, $$\Phi$$ insurance fund, $$f$$ funding rate, $$E$$ equity, $$M$$ margin requirements. Claims are grounded by inline equation references into the component models; where the corpus lacks an expression, the gap is named rather than papered over.

## The input process: $$u' = g(u, \text{sources})$$

The input is itself a guarded process $$u' = g(u, \text{prints})$$: an incoming print moves the trusted anchor only if it passes the single-step and path deviation guards ([(O.1)](/math-engine/oracle), [(O.2)](/math-engine/oracle), [(O.3)](/math-engine/oracle)); a silent feed trips the staleness predicate ([(O.4)](/math-engine/oracle)) and routes prints into a multi-print re-anchor confirmation with escalation ([(O.5)](/math-engine/oracle), [(O.6)](/math-engine/oracle)). One event map, given below, summarizes the whole process; the oracle component document carries the detail.

### Oracle print (input process)

The single event of the input process $$u' = g(u, \text{prints})$$. On a fresh anchor, an incoming print is accepted only if it passes the single-step guard ([(O.1)](/math-engine/oracle)) and, once the ten-print history is full, the path guard ([(O.2)](/math-engine/oracle) against [(O.3)](/math-engine/oracle)); acceptance moves $$(P\_{oracle}, t\_{last})$$ and appends to the history, rejection changes nothing. When the staleness predicate trips ([(O.4)](/math-engine/oracle)), prints route into the re-anchor protocol instead: each print within the active bound of the running candidate advances the confirmation counter, an inconsistent print restarts it ([(O.5)](/math-engine/oracle)), escalation widens the bound by print count or elapsed time ([(O.6)](/math-engine/oracle)), and promotion requires $$\max(k, 2) \ge 2$$ consecutive confirmations — while pending, the anchor and its timestamp never move. The engine sees only the result: $$u$$ steps by at most a factor $$1 + \theta$$ per fresh accept, or jumps on a confirmed re-anchor commit. Touches: $$P\_{oracle}, t\_{last}, \Omega$$ only — no engine coordinate.

$$
u' = \begin{cases} (P\_{new},\ t\_{now}) & \neg\mathrm{stale} \wedge \Delta\_{step} \le \theta \wedge \Delta\_{path} \le \theta\sqrt{N\_h} \quad (\text{fresh accept}) \ (P\_{cand},\ t\_{now}) & \mathrm{stale} \wedge n\_p' \ge \max(k, 2) \quad (\text{re-anchor promotion}) \ (P\_{oracle},\ t\_{last}) & \text{otherwise (reject / pending)} \end{cases} \tag{U.1}
$$

## The union: the closed loop

The union is the closed loop: the guarded input process feeds the engine, and the engine feeds back into its own input. Downward, every accepted print re-prices the entire state at once — equity, maintenance floors, funding premium, and the order collar all move with $$m\_k$$ — and the guards compose into the closed-loop bounds above: a fresh tick's motion is capped by $$\theta$$ before the engine ever sees it, so the equity shock per tick is bounded ([(O.1)](/math-engine/oracle) composed with [(O.8)](/math-engine/oracle) and [(M.5)](/math-engine/margin-math)), while a stale feed freezes exactly the anchor leg ([(O.4)](/math-engine/oracle)) and leaves liquidation live on the trade leg. Upward, every fill — including the cascade's own close fills — enters the five-trade window that shapes the trade reference ([(O.7)](/math-engine/oracle)): the engine influences the mark that will next judge its accounts, damped by $$(1-w)$$ and by the volume-weighted median's majority-volume requirement. This two-way coupling is what makes the composed system a genuine feedback loop rather than a filter followed by a plant: a liquidation prints fills, the fills move the trade leg, the moved mark re-evaluates the next account.

The composed system's long-run geometry: healthy states at an agreeing mark ($$m\_k = P\_{oracle,k}$$, zero premium) are equilibria — accrual adds zero, settlements transfer nothing ([(S.6)](/math-engine/settlement) emits no zero-amount transfer), the trigger is silent. Two absorbing regimes matter. Fund depletion: $$\Phi = 0$$ after an exhausting absorption ([(I.2)](/math-engine/insurance-fund) hits its floor exactly) arms ADL through both nested predicates and stays armed until a spread or penalty credit refills the balance ([(I.7)](/math-engine/insurance-fund), [(I.10)](/math-engine/insurance-fund)); the credit-before-absorb ordering (settled, code-verified) means a single liquidation carrying both profit and debt absorbs against $$\Phi + g$$, never the pre-credit balance. Liquidation stall: a negative cross share produces an oversized close that [(L.13)](/math-engine/liquidation-engine) rejects on every scan — a live-lock fixed point of the cascade in which the account remains triggered but untouched, exiting only when the mark or the share changes.

What the composition does *not* yet witness, named plainly. (1) adl\_settlement\_unmodeled: the ADL counterparty settlement map — closing price, per-counterparty size reduction, both sides' $$C\_a$$ updates, open-interest preservation — has no component expressions; the cascade's conservation invariant is scoped up to this leg, and the next cycle derives it from code (v0.0.9 audit, medium). (2) residual\_entry\_remark\_unwitnessed: the settle map values a partial liquidation's unfilled remainder at the mark and folds it into cash ([(L.16)](/math-engine/liquidation-engine) inside [(L.17)](/math-engine/liquidation-engine)), while the position layer retains the remainder's entry price ([(T.1)](/math-engine/position-tracker) is untouched by reductions) — consistency requires either excluding $$\Pi\_{\mathrm{res}}$$ from the cash settle or re-anchoring the residual's entry at the mark, and no expression witnesses either; conservation is therefore stated for fully-filled cascades. (3) cancel\_release\_arithmetic\_unwitnessed: the cancel/expiry map's per-order margin release has no corpus expression, though the guards that depend on it ([(M.16)](/math-engine/margin-math), [(M.14)](/math-engine/margin-math)) fix its required semantics. (4) adl\_arming\_condition\_ambiguity: the two arming predicates are nested, not symmetric — settle-amount firing implies threshold firing, and the surviving question is whether ADL executes when the threshold arms with $$D\_{\mathrm{adl}} = 0$$. Conversely, one previously-open gap is now closed: the liquidation penalty's fund-side destination is witnessed by the v0.0.9 expressions [(I.9)](/math-engine/insurance-fund), [(I.10)](/math-engine/insurance-fund), and [(I.11)](/math-engine/insurance-fund), retiring liquidation\_penalty\_sink as an open finding of this model.

## Invariants of the closed loop

### Bounded equity shock per fresh-anchor tick

$$
\big\lvert E(S, u') - E(S, u) \big\rvert ;\le; w, \theta, P\_{oracle} \sum\_i q\_i \qquad \text{(fresh-anchor accept branch only)} \tag{U.2}
$$

On the fresh-anchor accept branch of the input process, one oracle tick can shock an account's equity by at most $$w,\theta,P\_{oracle}\sum\_i q\_i$$ per market: the accepted anchor moves by at most $$\theta,P\_{oracle}$$ ([(O.1)](/math-engine/oracle)), the blend damps the anchor leg by $$w$$ ([(O.8)](/math-engine/oracle)), and equity is affine in the mark with slope bounded by total size ([(T.2)](/math-engine/position-tracker), [(M.5)](/math-engine/margin-math)). Explicitly excluded: the re-anchor commit branch — commits are bounded by $$\theta\_r/\theta\_e$$ relative to the *candidate*, not by $$\theta$$ relative to the prior anchor, and can exceed this bound (simulator result reanchor\_commit\_exceeds\_tick\_bound).

*Why it holds:* This invariant exists only at the composition: the engine alone accepts any $$u$$, and the oracle alone bounds prices, not equity. The accept guard rejects before any state write, so a committed fresh tick satisfies $$|\Delta P\_{oracle}| \le \theta P\_{oracle}$$; the mark is a convex combination, so $$|\Delta m\_k| = w,|\Delta P\_{oracle}|$$ with $$W\_k$$ unchanged by an oracle event; and $$E$$ is affine in $$m\_k$$ with slope $$\sum\_i s\_{i,k}$$, giving the bound by the triangle inequality. The exclusion is forced: re-anchor promotion moves the anchor to the candidate in one step, which the corpus bounds only relative to the candidate chain.

*Composes:* [*(O.1)*](/math-engine/oracle) [*(O.8)*](/math-engine/oracle) [*(T.2)*](/math-engine/position-tracker) [*(M.5)*](/math-engine/margin-math) [*(M.8)*](/math-engine/margin-math)

### Staleness freezes the anchor leg only

$$
\mathrm{stale} ;\Longrightarrow; (P\_{oracle}',, t\_{last}') = (P\_{oracle},, t\_{last}) \quad\text{and}\quad \lvert \Delta m\_k \rvert \le (1 - w), \lvert \Delta P\_{trade} \rvert \tag{U.3}
$$

While the anchor is stale ([(O.4)](/math-engine/oracle)), no pending print can move $$P\_{oracle}$$ or $$t\_{last}$$ — the re-anchor protocol persists only candidate bookkeeping until $$\max(k,2)$$ confirmations ([(O.5)](/math-engine/oracle)) — so the anchor leg of the mark is frozen and mark motion is confined to the trade leg, damped by $$(1-w)$$ and defended by the volume-weighted median ([(O.8)](/math-engine/oracle), [(O.7)](/math-engine/oracle)). Stated honestly: staleness does **not** pause liquidations — the trigger [(L.4)](/math-engine/liquidation-engine) keeps evaluating at the partially-frozen mark, whose trade leg the engine's own fills keep moving. The fail-closed property is confined to the anchor.

*Why it holds:* Only the trusted-accept path writes the anchor pair, and the pending branch returns without calling it, with the Trusted/Pending discriminant preventing a silent commit — so the anchor leg's contribution to $$\Delta m\_k$$ is zero under staleness. The mark is a convex combination, so the residual motion is exactly $$(1-w),\Delta P\_{trade}$$, and shifting $$P\_{trade}$$ requires manipulated prices to carry strictly more than half the window's volume. The liquidation half is a non-claim: no component expression conditions the trigger on freshness, so the composed system inherits liquidation-at-the-frozen-leg rather than a pause.

*Composes:* [*(O.4)*](/math-engine/oracle) [*(O.5)*](/math-engine/oracle) [*(O.8)*](/math-engine/oracle) [*(O.7)*](/math-engine/oracle) [*(L.4)*](/math-engine/liquidation-engine)

## Appendix — the complete formal system

This appendix is rendered mechanically from the state-space classification (`models/state-space.json`) — derived, not written. Its completeness claim is *checked*: the closure gate (`ci/closure.py`) verifies on every run that every corpus expression is classified, every state coordinate is written by an event and read somewhere, and every event map cites only defined coordinates. The state space factors into 16 coordinates (global fund and cash books; per-market book, trade window, premium accumulator, oracle anchor and re-anchor pending block; per-account collateral and reserved margin; per-position size/entry/direction and funding accumulator), 9 inputs, 20 parameters, and 54 derived observables, with all 133 corpus variables and 84 expressions classified totally across 10 events. Alphabet completion added adl\_execution, oracle\_reanchor\_step, oracle\_reanchor\_commit — including adl\_execution emitted with empty writes so the closure gate keeps flagging the unmodeled ADL counterparty settlement map. Deliberately uncited update maps (deposit/withdrawal transfer, cancel removal and reservation release, anchor assignments, trade-window push, funding resets) remain named gaps rather than inventions.

### B.1 The state vector

**per-market**

| Coordinate           | Symbol            | Units                    | Owner  | Description                                                                                                                                                                                                                                                 |
| -------------------- | ----------------- | ------------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `trade_window`       | $$W\_k$$          | five (price, size) pairs | oracle | The last five executed fills of the market, written by the fill map (including liquidation close fills) and read by the volume-weighted median trade reference — the engine-owned leg of the mark blend.                                                    |
| `oracle_anchor`      | $$P\_{oracle,k}$$ | USDX per unit of asset   | oracle | The trusted anchor price of the market — the engine's exogenous input process state; moved only by the guarded fresh-accept and re-anchor-commit branches of the oracle map, never by any engine event.                                                     |
| `anchor_timestamp`   | $$t\_{last,k}$$   | milliseconds             | oracle | Timestamp of the last trusted anchor update, read by the staleness predicate; frozen together with the anchor while a re-anchor is pending.                                                                                                                 |
| `oracle_guard_state` | $$\Omega\_k$$     | prints and milliseconds  | oracle | The input process's defense bookkeeping: the ten-print history (whose oldest element the path check reads) and the pending re-anchor block (candidate price, pending print count, confirmation counter, opening timestamp); touched only by the oracle map. |

### B.2 Inputs and parameters

Inputs are exogenous — they arrive from outside the state; parameters are constants of market or system configuration.

**Inputs**

| Input                      | Symbol       | Units                  | Description                                                                                                                     |
| -------------------------- | ------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `oracle_print_price`       | $$P\_{new}$$ | USDX per unit of asset | An incoming external oracle print being validated against the anchor or the pending re-anchor candidate.                        |
| `wall_clock_time`          | $$t\_{now}$$ | milliseconds           | Feed-supplied timestamp of the incoming print or staleness evaluation, in Unix milliseconds.                                    |
| `time_delta`               | $$\Delta t$$ | seconds                | Exogenous elapsed time since the previous funding premium sample; non-advancing samples are ignored.                            |
| `order_quantity`           | $$q$$        | base units             | Quantity of an arriving order; must be a lot multiple to pass admission.                                                        |
| `order_limit_price`        | $$P\_{lim}$$ | USDX per base unit     | Limit price of an arriving limit order; must be strictly positive, tick-aligned, and inside the mark collar.                    |
| `order_signed_size`        | $$o$$        | base units, signed     | Signed size of an arriving order (buy positive, sell negative), read by the added-exposure computation.                         |
| `max_slippage_bps`         | $$\beta$$    | basis points           | Taker-supplied per-order slippage cap on a market order; absent means no cap.                                                   |
| `preview_requested_qty`    | $$q\_{req}$$ | base units             | Quantity requested by a hypothetical market order in the read-only VWAP preview; undefined for non-positive requests.           |
| `external_transfer_amount` | $$x$$        | USDX                   | External USDX amount of a deposit or withdrawal; not a corpus variable — carried by the composition's deposit/withdrawal event. |

**Parameters**

| Parameter                    | Symbol        | Units                         | Scope       | Description                                                                                                                                                                             |
| ---------------------------- | ------------- | ----------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `funding_rate_cap`           | $$c$$         | fraction per funding interval | per-market  | Symmetric cap on the funding rate; specified at 0.5% per settlement window.                                                                                                             |
| `adl_threshold`              | $$\kappa$$    | USDX                          | per-market  | ADL trigger threshold on the fund balance; default zero arms ADL only at full depletion.                                                                                                |
| `maintenance_margin_rate`    | $$r\_m$$      | dimensionless ratio           | per-market  | Market maintenance margin rate; strictly less than the initial margin rate.                                                                                                             |
| `initial_margin_rate`        | $$r\_i$$      | dimensionless ratio           | per-market  | Market initial margin rate, equal to one over the market's maximum leverage.                                                                                                            |
| `account_leverage`           | $$L$$         | multiplier                    | per-account | Account-selected leverage per market (integer >= 1, validated before storage); user configuration with no state-mutating expression in the corpus, hence a parameter, not a coordinate. |
| `tick_size`                  | $$\delta$$    | USDX per base unit            | per-market  | Minimum price increment; non-positive tick disables alignment.                                                                                                                          |
| `lot_size`                   | $$\ell$$      | base units                    | per-market  | Market lot size; order and position sizes are integer multiples of it; zero disables the alignment check.                                                                               |
| `taker_fee_bps`              | $$b\_t$$      | basis points                  | per-market  | Taker fee rate charged on fill notional.                                                                                                                                                |
| `maker_rebate_bps`           | $$b\_m$$      | basis points                  | per-market  | Maker rebate rate, stored negative by convention; applied by absolute value.                                                                                                            |
| `liquidation_penalty_bps`    | $$b\_{liq}$$  | basis points                  | per-market  | Penalty rate applied to the notional of liquidation fills and routed to the insurance fund.                                                                                             |
| `price_band_bps`             | $$b\_{band}$$ | basis points                  | per-market  | Maximum admissible relative deviation of a limit price from the mark (the admission collar).                                                                                            |
| `oracle_deviation_threshold` | $$\theta$$    | dimensionless fraction        | per-market  | Single-step deviation threshold for accepting an oracle print against the anchor.                                                                                                       |
| `oracle_history_size`        | $$N\_h$$      | prints                        | global      | Fixed size of the rolling price-update window used by the path-manipulation check (HISTORY\_SIZE = 10).                                                                                 |
| `oracle_staleness_seconds`   | $$\tau\_s$$   | seconds                       | per-market  | Staleness threshold: the anchor is stale strictly beyond this many seconds since the last trusted update.                                                                               |
| `reanchor_max_deviation`     | $$\theta\_r$$ | dimensionless fraction        | per-market  | Per-step consistency bound for re-anchor confirmations against the running candidate.                                                                                                   |
| `escalation_max_deviation`   | $$\theta\_e$$ | dimensionless fraction        | per-market  | Widened per-step bound applied once the escalation trigger has fired.                                                                                                                   |
| `required_confirmations`     | $$k$$         | prints                        | per-market  | Consecutive mutually-consistent prints required to promote a re-anchor; floored at 2 effectively.                                                                                       |
| `escalation_prints`          | $$N\_e$$      | prints                        | per-market  | Print-count arm of the re-anchor escalation trigger.                                                                                                                                    |
| `escalation_seconds`         | $$\tau\_e$$   | seconds                       | per-market  | Wall-clock arm of the re-anchor escalation trigger, measured from the opening of the pending sequence.                                                                                  |
| `oracle_mark_weight`         | $$w$$         | dimensionless fraction        | per-market  | Oracle weight in the mark blend; unit-interval, default 0.95 (oracle-dominant).                                                                                                         |

### B.3 Derived observables

Pure functions of state, inputs, and parameters — recomputed, never persisted.

| Quantity                   | Symbol              | Units                 | Defined by                                | Description                                                                                                                                                                                                                |
| -------------------------- | ------------------- | --------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mark_price`               | $$m\_k$$            | USDX per base unit    | [(O.8)](/math-engine/oracle)              | Convex blend of the trusted anchor and the volume-weighted trade reference; equals the anchor when no trade reference exists. Recomputed on demand, never persisted.                                                       |
| `trade_ref`                | $$P\_{trade}$$      | USDX per base unit    | [(O.7)](/math-engine/oracle)              | Volume-weighted median price of the five-trade window; falls back to the last trade price when the window is short.                                                                                                        |
| `premium_index`            | $$p$$               | dimensionless         | [(F.1)](/math-engine/funding-rate)        | Fractional deviation of the mark from the anchor at a sample; skipped when the anchor is non-positive.                                                                                                                     |
| `funding_rate`             | $$f$$               | fraction per interval | [(F.3)](/math-engine/funding-rate)        | Clamped time-weighted average premium, recomputed fresh from (A, T) at settlement with the T = 0 branch returning zero.                                                                                                    |
| `funding_payment`          | $$\Pi^f$$           | USDX                  | [(F.4)](/math-engine/funding-rate)        | Signed per-position funding payment sigma q m f; simultaneously the settlement event's collateral update delta.                                                                                                            |
| `unrealized_pnl`           | $$\mathrm{uPnL}$$   | USDX                  | [(T.2)](/math-engine/position-tracker)    | Per-position mark-to-market PnL; the account total is its sum over open positions (coherence-merged with liquidation-engine.fresh\_unrealized\_pnl).                                                                       |
| `account_equity`           | $$E$$               | USDX                  | [(M.8)](/math-engine/margin-math)         | Collateral plus mark-to-market PnL (net of funding integrals in the portfolio form); coherence-merged across margin-math.equity, margin-math.portfolio\_equity, and liquidation-engine.account\_equity.                    |
| `maintenance_margin`       | $$M\_m$$            | USDX                  | [(M.4)](/math-engine/margin-math)         | Maintenance margin at the mark; coherence-merged with the portfolio and liquidation-engine instances.                                                                                                                      |
| `initial_margin`           | $$M\_i$$            | USDX                  | [(M.2)](/math-engine/margin-math)         | Initial margin at the mark (stamped allocated margin where set); coherence-merged with the portfolio instance.                                                                                                             |
| `available_margin`         | $$M\_{avail}$$      | USDX                  | [(M.7)](/math-engine/margin-math)         | Equity minus total initial margin held; can be negative; gates order admission, not withdrawal.                                                                                                                            |
| `added_exposure`           | $$\Delta q$$        | base units            | [(M.11)](/math-engine/margin-math)        | Magnitude of newly-opened exposure an order adds: growth on increase, zero on reduce/close, the whole new side on a flip.                                                                                                  |
| `admission_added_margin`   | $$M\_{add}$$        | USDX                  | [(M.12)](/math-engine/margin-math)        | Initial margin charged on added exposure at the mark and effective rate; also the amount written into the reservation at admission.                                                                                        |
| `isolated_margin_cushion`  | $$C\_{iso}$$        | USDX                  | [(L.5)](/math-engine/liquidation-engine)  | Collateral backing an isolated position: the margin allocated at fill time if recorded, otherwise the open-time initial margin at market rate; the trigger ((L.4)) and the liquidation pricing both use this single value. |
| `bankruptcy_price`         | $$p\_b$$            | USDX per base unit    | [(L.8)](/math-engine/liquidation-engine)  | Price at which the position's backing collateral is exactly exhausted; coherence-merged with position-tracker.bankruptcy\_price; may be zero or negative before alignment.                                                 |
| `aligned_bankruptcy_price` | $$\tilde{p}\_b$$    | USDX per base unit    | [(L.9)](/math-engine/liquidation-engine)  | Tick-aligned close-order limit: floor for sells (liquidation-engine.aligned\_price\_sell), ceil for buys (liquidation-engine.aligned\_price\_buy), clamped to one tick.                                                    |
| `liquidation_price`        | $$p\_{liq}$$        | USDX per base unit    | [(T.8)](/math-engine/position-tracker)    | Analytically-solved mark at which equity meets the maintenance requirement; display/analysis, neither moves state nor gates events.                                                                                        |
| `cross_collateral_share`   | $$s\_i$$            | USDX                  | [(L.6)](/math-engine/liquidation-engine)  | Loss-proportional share of the shared cross pool per market, with the remainder folded into the largest-loss position's share.                                                                                             |
| `collateral_share_sum`     | $$S$$               | USDX                  | \`\`                                      | Sum of the proportional shares before the remainder fold; prose-defined only — no corpus expression id (read by the remainder fold).                                                                                       |
| `position_loss`            | $$\ell\_i$$         | USDX                  | \`\`                                      | max(0, -uPnL\_i) per market with entry-price fallback; defined only in variable prose, no corpus expression id.                                                                                                            |
| `total_loss`               | $$\mathcal{L}$$     | USDX                  | \`\`                                      | Sum of position losses across the positions liquidated together; prose-defined only.                                                                                                                                       |
| `safe_size`                | $$q\_{safe}$$       | base units            | [(L.11)](/math-engine/liquidation-engine) | Largest lot-multiple size whose 1.5x-padded initial margin the collateral share covers.                                                                                                                                    |
| `liquidation_qty`          | $$q\_{liq}$$        | base units            | [(L.12)](/math-engine/liquidation-engine) | Close-order quantity: full size in Full mode or degenerate cases, else reduction to safe size.                                                                                                                             |
| `fill_quantity`            | $$q^{\star}$$       | base units            | [(B.6)](/math-engine/order-book)          | Quantity of a single fill: min of taker and front-maker remainders; coherence-merged with settlement.size and position-tracker.fill\_quantity.                                                                             |
| `fill_price`               | $$P^{\star}$$       | USDX per base unit    | [(B.7)](/math-engine/order-book)          | Price of a single fill — always the maker's limit price; coherence-merged with settlement.price and position-tracker/liquidation-engine fill prices.                                                                       |
| `closed_quantity`          | $$q\_c$$            | base units            | [(T.3)](/math-engine/position-tracker)    | Portion of an opposing fill that closes existing size: min(size, fill quantity).                                                                                                                                           |
| `total_filled`             | $$Q\_f$$            | base units            | \`\`                                      | Sum of the liquidation close order's fill quantities; within-event accumulation, prose-defined only.                                                                                                                       |
| `spread_profit`            | $$g$$               | USDX                  | [(L.14)](/math-engine/liquidation-engine) | Positive part of fills' price improvement over the aligned bankruptcy price, summed over fills; credited to the fund.                                                                                                      |
| `realized_fill_pnl`        | $$\Pi\_{fill}$$     | USDX                  | [(L.15)](/math-engine/liquidation-engine) | Signed realized PnL of liquidation fills, each at its own fill price; a component of the cascade's per-market settlement X\_i.                                                                                             |
| `residual_unfilled_pnl`    | $$\Pi\_{res}$$      | USDX                  | [(L.16)](/math-engine/liquidation-engine) | Mark-valued PnL of the unfilled remainder of the close order; zero on complete fill.                                                                                                                                       |
| `bad_debt`                 | $$D$$               | USDX                  | [(L.17)](/math-engine/liquidation-engine) | Non-negative shortfall after fills-aware settlement against the collateral share; drawn from the fund, then ADL.                                                                                                           |
| `absorbed_amount`          | $$D\_{abs}$$        | USDX                  | [(I.1)](/math-engine/insurance-fund)      | min(bad debt, fund balance): the delta by which the fund and its absorption ledger move.                                                                                                                                   |
| `adl_settle_amount`        | $$D\_{adl}$$        | USDX                  | [(I.4)](/math-engine/insurance-fund)      | Shortfall handed to ADL after the fund is drained; an instruction is emitted only when strictly positive.                                                                                                                  |
| `adl_priority_score`       | $$\rho$$            | dimensionless         | [(L.18)](/math-engine/liquidation-engine) | ADL ranking score pi \* L, descending with deterministic account-id tie-break; coherence-merged with the insurance-fund instance.                                                                                          |
| `adl_pnl_percent`          | $$\pi$$             | fraction              | \`\`                                      | ADL candidate's unrealized PnL as a fraction of position value; no corpus expression defines it — a gap for the closure gate.                                                                                              |
| `liquidation_penalty_owed` | $$\Lambda\_{owed}$$ | USDX                  | [(S.4)](/math-engine/settlement)          | Penalty owed on filled liquidation notional at the market's penalty rate.                                                                                                                                                  |
| `penalty_charged`          | $$\Lambda$$         | USDX                  | [(I.9)](/math-engine/insurance-fund)      | Penalty actually debited/credited: the owed amount capped at available collateral, so the pair cannot mint USDX.                                                                                                           |
| `fill_notional`            | $$V^{\star}$$       | USDX                  | [(S.1)](/math-engine/settlement)          | Notional of a single fill, size times price, exact decimal.                                                                                                                                                                |
| `net_exchange_revenue`     | $$V$$               | USDX                  | [(S.5)](/math-engine/settlement)          | Settlement-record closure: total taker fees minus total maker rebates.                                                                                                                                                     |
| `margin_ratio`             | $$\rho\_M$$         | dimensionless         | [(M.6)](/math-engine/margin-math)         | Equity over total notional; undefined at zero notional; diagnostic — gates nothing in the corpus.                                                                                                                          |
| `max_position_size`        | $$q\_{max}$$        | base units            | [(M.15)](/math-engine/margin-math)        | Largest lot-aligned position openable with given collateral at the market rate; sizing/display analysis.                                                                                                                   |
| `withdrawable_collateral`  | $$W\_{max}$$        | USDX                  | [(M.16)](/math-engine/margin-math)        | Full realized collateral for a flat, unreserved account, zero otherwise; the withdrawal guard's cap.                                                                                                                       |
| `best_bid`                 | $$P\_b$$            | USDX per base unit    | \`\`                                      | Highest resting bid — a structural readout of the order\_book coordinate; no corpus expression id.                                                                                                                         |
| `best_ask`                 | $$P\_a$$            | USDX per base unit    | \`\`                                      | Lowest resting ask — a structural readout of the order\_book coordinate; no corpus expression id.                                                                                                                          |
| `mid_price`                | $$P\_{mid}$$        | USDX per base unit    | [(B.4)](/math-engine/order-book)          | Midpoint of best bid and ask, snapshotted once at market-order submission for the slippage cap; undefined when either side is empty.                                                                                       |
| `slippage_span`            | $$\Delta\_{slip}$$  | USDX per base unit    | [(B.9)](/math-engine/order-book)          | Half-width of the admissible VWAP band anchored at the mid-price snapshot.                                                                                                                                                 |
| `available_qty`            | $$Q\_{avail}$$      | base units            | \`\`                                      | Pre-match opposing liquidity at prices satisfying the taker's limit; within-event readout of the book, prose-defined only.                                                                                                 |
| `taker_remaining`          | $$q\_t$$            | base units            | \`\`                                      | Taker's unfilled remainder during the matching walk; within-event intermediate, prose-defined only.                                                                                                                        |
| `running_notional`         | $$V\_k$$            | USDX                  | \`\`                                      | Cumulative notional of fills accepted so far in a market-order walk; within-event accumulator, prose-defined only.                                                                                                         |
| `running_filled`           | $$Q\_k$$            | base units            | \`\`                                      | Cumulative quantity of fills accepted so far in a market-order walk; within-event accumulator, prose-defined only.                                                                                                         |
| `walk_notional`            | $$V$$               | USDX                  | \`\`                                      | Total notional of a hypothetical price-time-priority walk for the VWAP preview; prose-defined only.                                                                                                                        |
| `vwap_estimate`            | $$\overline{P}$$    | USDX per base unit    | [(B.11)](/math-engine/order-book)         | Read-only market-order VWAP preview; undefined when liquidity cannot cover the request.                                                                                                                                    |
| `funding_integral`         | $$\varphi$$         | USDX                  | \`\`                                      | Per-position accumulated funding read by portfolio equity; identically zero under the composed settle-and-reset convention (phi = 0) — no accrual map exists in the corpus, by design.                                     |
| `total_notional`           | $$N$$               | USDX                  | \`\`                                      | Sum of size times mark over open positions; prose-defined only, the margin ratio's denominator.                                                                                                                            |
| `open_positions`           | $$n\_{pos}$$        | count                 | \`\`                                      | Count of the account's open positions — a structural readout of the position coordinates; no corpus expression id.                                                                                                         |

### B.4 The transition matrix

**Guards**

| Event                    | Guard                                                                                                                                                                                                                                                          | Reads                                                     |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `oracle_print_accept`    | \neg stale (oracle.is\_stale = 0) \wedge \Delta\_{step} \le \theta (oracle.single\_step\_deviation) \wedge (\|H\| = N\_h \Rightarrow \Delta\_{path} \le \theta\sqrt{N\_h}) (oracle.path\_deviation vs oracle.path\_threshold)                                  | `oracle_anchor`, `anchor_timestamp`, `oracle_guard_state` |
| `oracle_reanchor_step`   | stale (oracle.is\_stale = 1) \wedge print arrives \wedge confirmations after this step < \max(k, 2); active per-step bound is \theta\_r, widened to \theta\_e once the escalation trigger fires (oracle.reanchor\_step\_deviation, oracle.escalation\_trigger) | `oracle_guard_state`, `oracle_anchor`, `anchor_timestamp` |
| `oracle_reanchor_commit` | stale \wedge consecutive mutually-consistent confirmations \ge \max(k, 2) (oracle.reanchor\_step\_deviation within the active bound on the promoting print)                                                                                                    | `oracle_guard_state`, `anchor_timestamp`                  |

**Writes** — rows are state coordinates, columns are events; a cell cites the component equation defining that update; `·` means provably untouched.

| Coordinate                        | `oracle_print_accept` | `oracle_reanchor_step` | `oracle_reanchor_commit` |
| --------------------------------- | --------------------- | ---------------------- | ------------------------ |
| `oracle_anchor` (per-market)      | ✓                     | ·                      | ✓                        |
| `anchor_timestamp` (per-market)   | ✓                     | ·                      | ✓                        |
| `oracle_guard_state` (per-market) | ✓                     | ✓                      | ✓                        |

## References

* The engine alone: [global](/math-engine/global)
* The oracle component model: [oracle](/math-engine/oracle)


# Funding Rate

Perpetual futures never expire, so a periodic cash transfer between longs and shorts — funding — anchors the contract's mark price to the oracle (spot) price. During each funding interval the Exchange samples the instantaneous premium index $$(P\_{mark} - P\_{oracle})/P\_{oracle}$$ and accumulates it weighted by the time elapsed since the previous sample. At any point in the interval the funding rate is the time-weighted average premium, clamped to a symmetric cap $$\pm c$$. At settlement, each position pays or receives an amount proportional to its notional value at the mark price: with a positive rate, longs pay shorts; with a negative rate, shorts pay longs. The construction is exactly zero-sum between matched long and short open interest.

![The funding rate is a fan-in of the premium accumulator and the interval clock, and settlement feeds back both into a fresh accumulator and into the positioning pressure that moves the next premium.](/files/fhOhTBi5KwZFG60k6O6S)

*The funding rate is a fan-in of the premium accumulator and the interval clock, and settlement feeds back both into a fresh accumulator and into the positioning pressure that moves the next premium.*

## Setting

| Symbol          | Name                 | Description                                                                                                                                                                                                                      | Units                                         | Domain             |
| --------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | ------------------ |
| $$P\_{oracle}$$ | oracle\_price        | External index (oracle) price of the underlying asset. Samples with a non-positive oracle price are skipped by the implementation, so the effective domain is strictly positive.                                                 | USDX per unit of asset                        | (0, ∞)             |
| $$P\_{mark}$$   | mark\_price          | The Exchange's mark price for the perpetual market at the sample or settlement time.                                                                                                                                             | USDX per unit of asset                        | (0, ∞)             |
| $$\Delta t$$    | time\_delta\_secs    | Seconds elapsed since the previous sample in the interval. Samples at or before the previous sample time are ignored, so the effective domain is strictly positive. Derived in code from millisecond timestamps divided by 1000. | seconds                                       | (0, ∞)             |
| $$A$$           | accumulated\_premium | Running sum of premium-index samples weighted by their time deltas, $$\sum\_i p\_i , \Delta t\_i$$, over the current funding interval. Reset to zero at each interval boundary.                                                  | dimensionless-seconds (premium × seconds)     | unbounded          |
| $$T$$           | total\_time\_secs    | Total elapsed time in the current interval, from the interval start to the most recent sample. Derived, not stored: when zero (no samples yet) the implementation short-circuits and returns a zero rate rather than dividing.   | seconds                                       | (0, ∞)             |
| $$c$$           | funding\_rate\_cap   | Per-market symmetric cap on the funding rate, stored in market parameters. The specified value is 0.5% per settlement window.                                                                                                    | dimensionless (fraction per funding interval) | (0, ∞)             |
| $$f$$           | funding\_rate        | The clamped funding rate applied to positions at settlement; always lies in $$\[-c, +c]$$.                                                                                                                                       | dimensionless (fraction per funding interval) | \[-0.0075, 0.0075] |
| $$S$$           | size                 | Absolute position size in units of the underlying asset (always non-negative; direction is carried separately by the position side).                                                                                             | units of asset                                | \[0, ∞)            |
| $$\sigma$$      | side\_sign           | Direction indicator for the position: $$+1$$ for a long position, $$-1$$ for a short position. Encodes the match on position side in the implementation.                                                                         | dimensionless                                 | \[-1, 1]           |

## The mechanism

### Premium Index Sampling

Each oracle tick produces one sample of the instantaneous premium index: the relative deviation of the mark price from the oracle price. A positive premium means the perpetual trades rich to the index (longs are crowded); a negative premium means it trades cheap. The implementation skips any sample whose oracle price is not strictly positive, leaving the interval state unchanged, so the division is always well defined.

$$
p = \frac{P\_{mark} - P\_{oracle}}{P\_{oracle}} \tag{F.1}
$$

Samples arrive at an irregular cadence, so each premium index (F.1) is weighted by the time elapsed since the previous sample before being added to the interval accumulator $$A$$. This makes the accumulator the exact time integral of the (piecewise-constant) premium index over the interval, independent of sampling frequency: 1-minute and 5-second cadences produce identical averages for the same premium path. Samples with a timestamp at or before the previous sample time are ignored, so $$\Delta t$$ is strictly positive.

$$
A \leftarrow A + p \cdot \Delta t \tag{F.2}
$$

### Funding Rate

The funding rate at any point in the interval is the time-weighted average premium — the accumulator (F.2) divided by the total elapsed time $$T$$ — clamped to the symmetric per-market cap $$\pm c$$. The clamp bounds the wealth transfer per settlement window regardless of how dislocated the mark price becomes. When no time has elapsed in the interval ($$T = 0$$, i.e. no samples yet), the implementation returns zero rather than dividing by zero. The predicted funding rate published mid-interval is defined to be this same value: the current average is the best estimate of the final rate.

$$
f = \operatorname{clamp}!\left(\frac{A}{T},; -c,; +c\right) \tag{F.3}
$$

### Funding Payments

At settlement each open position exchanges cash proportional to its notional value at the mark price. The signed amount is the position's notional $$S \cdot P\_{mark}$$ times the funding rate (F.3), with the sign flipped for shorts via $$\sigma$$. The convention is: a positive amount means the account pays; a negative amount means the account receives. So with a positive rate ($$f > 0$$, perpetual rich to index) longs pay and shorts receive, and with a negative rate shorts pay and longs receive. For equal long and short size at the same mark and rate, the two amounts cancel exactly.

$$
\Pi = \sigma \cdot S \cdot P\_{mark} \cdot f \tag{F.4}
$$

## Invariants

* The funding rate is always bounded: $$-c \le f \le +c$$ for every interval state ((F.3)).
* Zero premium implies zero rate: if $$P\_{mark} = P\_{oracle}$$ for the whole interval, then $$A = 0$$ and $$f = 0$$.
* When not clamped, the sign of the rate matches the sign of the average premium: a perpetual trading rich to the index yields $$f > 0$$, trading cheap yields $$f < 0$$.
* Funding is exactly zero-sum for matched open interest: for equal long and short size at the same mark price and rate, $$\Pi\_{long} + \Pi\_{short} = 0$$ ((F.4)); verified by property-based testing across the full rate range.
* Payments are linear in position size: tripling $$S$$ triples $$\Pi$$ exactly.
* The rate is well defined at all times: with no samples in the interval ($$T = 0$$) the rate is exactly $$0$$, never a division error.
* Samples with non-positive oracle price or non-increasing timestamps leave the interval state unchanged.
* The TWAP is sampling-cadence invariant for a constant premium: any positive sampling frequency yields $$f$$ equal to the constant premium index (when below the cap).

## Worked example

Consider the BTC perpetual over one 8-hour funding interval with the oracle steady at $$P\_{oracle} = 50{,}000$$. For the first 4 hours the mark price is $$50{,}020$$, a premium index of $$p\_1 = 20/50{,}000 = 0.0004$$ per (F.1); for the last 4 hours the mark is $$50{,}035$$, so $$p\_2 = 35/50{,}000 = 0.0007$$. Sampling once per minute, each phase contributes its premium times its duration to the accumulator per (F.2): $$A = 0.0004 \times 14{,}400 + 0.0007 \times 14{,}400 = 5.76 + 10.08 = 15.84$$ premium-seconds.

The interval spans $$T = 28{,}800$$ seconds, so the raw time-weighted average premium is $$A/T = 15.84 / 28{,}800 = 0.00055$$. With a cap of $$c = 0.0075$$ this is far inside the band, so the clamp in (F.3) does not bind and the settled funding rate is $$f = 0.00055$$ — exactly the duration-weighted mean of the two premium phases, $$(0.0004 + 0.0007)/2$$.

Now settle a 2 BTC long at mark $$50{,}035$$. Its notional is $$2 \times 50{,}035 = 100{,}070$$ USDX, so per (F.4) the payment is $$\Pi = +1 \times 100{,}070 \times 0.00055 \approx 55.04$$ USDX — positive, so the long pays. A 2 BTC short at the same mark has $$\Pi \approx -55.04$$ USDX and receives the same amount: the transfer is exactly zero-sum. Had the market instead sustained a 10% premium all interval, the raw average $$0.10$$ would have been clamped to the cap and every position would settle at $$f = c$$.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression            | Input                | Elasticity ε |
| --------------------- | -------------------- | ------------ |
| `premium_index`       | mark\_price          | 1.43e+03     |
| `premium_index`       | oracle\_price        | -1.43e+03    |
| `sample_contribution` | mark\_price          | 1.43e+03     |
| `sample_contribution` | oracle\_price        | -1.43e+03    |
| `sample_contribution` | time\_delta\_secs    | 1            |
| `funding_rate`        | accumulated\_premium | 1            |
| `funding_rate`        | total\_time\_secs    | -1           |
| `funding_rate`        | funding\_rate\_cap   | 0            |
| `funding_payment`     | side\_sign           | 1            |
| `funding_payment`     | size                 | 1            |
| `funding_payment`     | mark\_price          | 1            |
| `funding_payment`     | funding\_rate        | 1            |

![Sensitivity tornado — Premium index](/files/0rmlq9xOdNUQCEyMqtoG)

![Sensitivity tornado — Funding rate (clamped TWAP)](/files/D5A0l56shLjrGV6vccNq)

![Sensitivity tornado — Funding payment](/files/4NjHeksTEZDaxcAiyABd)

### Response curves

![The instantaneous premium index is linear in the mark price and crosses zero exactly where mark equals the fixed $50,000 oracle price.](/files/1SUb1mnSA8fBVdGHRDph)

*The instantaneous premium index is linear in the mark price and crosses zero exactly where mark equals the fixed $50,000 oracle price.*

![Over a full 8-hour interval (T = 28,800 s) the funding rate rises linearly with the accumulated premium until the average hits the ±0.0075 cap, where it saturates.](/files/tqWwYRMIzFSX3NdUVsJg)

*Over a full 8-hour interval (T = 28,800 s) the funding rate rises linearly with the accumulated premium until the average hits the ±0.0075 cap, where it saturates.*

![At a positive 0.05% rate and $50,000 mark price, payments scale linearly with size and are exactly mirrored between long and short, so matched open interest nets to zero.](/files/D9Gh9DRfsPPKs892Ov1G)

*At a positive 0.05% rate and $50,000 mark price, payments scale linearly with size and are exactly mirrored between long and short, so matched open interest nets to zero.*

### Parameter space

Joint parameter effects evaluated from the verified expressions over 2-D grids.

![The clamp carves wedge-shaped saturation zones where |accumulated premium / time| exceeds the ±0.75% cap, so short accumulation windows saturate at far smaller premiums; cap held at 0.0075.](/files/ji9aNK1hpC7XtKG2JwFF)

*The clamp carves wedge-shaped saturation zones where |accumulated premium / time| exceeds the ±0.75% cap, so short accumulation windows saturate at far smaller premiums; cap held at 0.0075.*

![The bilinear size × rate interaction produces hyperbolic level sets of equal payment, with the sign flip at rate = 0 splitting payers from receivers; mark price held at 50,000 for a long position.](/files/NAwQ05XWubuRagsGi4Av)

*The bilinear size × rate interaction produces hyperbolic level sets of equal payment, with the sign flip at rate = 0 splitting payers from receivers; mark price held at 50,000 for a long position.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [insurance-fund](/math-engine/insurance-fund)
* Sibling model: [liquidation-engine](/math-engine/liquidation-engine)
* Sibling model: [margin-math](/math-engine/margin-math)
* Sibling model: [oracle](/math-engine/oracle)
* Sibling model: [order-book](/math-engine/order-book)
* Sibling model: [position-tracker](/math-engine/position-tracker)
* Sibling model: [settlement](/math-engine/settlement)


# Margin Math

Every position on the Exchange is backed by collateral. This document derives the complete margin model from the implementation: the initial margin reserved when exposure is opened, the maintenance margin that must be preserved to keep it open, and the account-level aggregates — equity, margin ratio, and available margin — that gate every new order. The model is linear in size and price, parameterized per market by an initial margin rate $$r\_{im}$$ and a maintenance margin rate $$r\_{mm}$$ with $$r\_{mm} < r\_{im}$$, and extended per position by an optional leverage selection that can only make requirements stricter, never looser.

![All per-position quantities fan in to a single account-level equity, which fans back out into the two derived quantities (available margin, margin ratio) that respectively gate admission and trigger liquidation — and every admitted order feeds new position state back into that fan-in.](/files/03EWErZK8wafwLLRbAS1)

*All per-position quantities fan in to a single account-level equity, which fans back out into the two derived quantities (available margin, margin ratio) that respectively gate admission and trigger liquidation — and every admitted order feeds new position state back into that fan-in.*

## Setting

| Symbol         | Name                      | Description                                                                                                                                               | Units                 | Domain    |
| -------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | --------- |
| $$q$$          | size                      | Position or order size (unsigned magnitude).                                                                                                              | base units (e.g. BTC) | \[0, ∞)   |
| $$P$$          | price                     | Mark price used for margin computation; falls back to the position's entry price when no fresh mark is available.                                         | USDX per base unit    | (0, ∞)    |
| $$r\_{im}$$    | initial\_margin\_rate     | Market initial margin rate, equal to one over the market's maximum leverage.                                                                              | dimensionless ratio   | (0, 1]    |
| $$r\_{mm}$$    | maintenance\_margin\_rate | Market maintenance margin rate; strictly less than the initial margin rate.                                                                               | dimensionless ratio   | (0, 1]    |
| $$L$$          | leverage                  | Account-selected leverage for a market. Unset or zero falls back to the market rate; values of 1 or more map to a rate of 1/L floored at the market rate. | multiplier            | \[1, ∞)   |
| $$s\_0$$       | existing\_signed          | Signed size of the resting position before an order: positive long, negative short, zero flat.                                                            | base units, signed    | unbounded |
| $$o$$          | order\_signed             | Signed size of the order: a buy is positive, a sell is negative.                                                                                          | base units, signed    | unbounded |
| $$C$$          | collateral                | Account collateral on deposit.                                                                                                                            | USDX                  | \[0, ∞)   |
| $$\Pi$$        | total\_unrealized\_pnl    | Sum of unrealized profit and loss across the account's open positions.                                                                                    | USDX                  | unbounded |
| $$N$$          | total\_notional           | Sum of position size times mark price across open positions.                                                                                              | USDX                  | (0, ∞)    |
| $$M\_{used}$$  | total\_initial\_margin    | Sum of initial margin held for existing positions — the stamped allocated margin where set, otherwise the market rate against the current mark.           | USDX                  | \[0, ∞)   |
| $$M\_{avail}$$ | available\_margin         | Equity minus total initial margin held; can be negative when profitable positions offset requirements elsewhere.                                          | USDX                  | unbounded |
| $$\ell$$       | lot\_size                 | Market lot size; position sizes are integer multiples of it.                                                                                              | base units            | (0, ∞)    |
| $$m$$          | mark                      | Fresh mark price for a position's market.                                                                                                                 | USDX per base unit    | (0, ∞)    |
| $$e$$          | entry\_price              | Volume-weighted entry price of a position.                                                                                                                | USDX per base unit    | (0, ∞)    |
| $$s$$          | signed\_size              | Signed position size: +size for a long, −size for a short.                                                                                                | base units, signed    | unbounded |
| $$\varphi$$    | funding\_integral         | Accumulated signed funding on a position; positive means the account has paid funding (a debit against equity), negative means it has received funding.   | USDX                  | unbounded |

## The mechanism

### Margin rates

Each market carries a default initial margin rate $$r\_{im} = 1/L\_{max}$$, where $$L\_{max}$$ is the market's maximum leverage. An account may select a lower leverage $$L$$ for a market, which tightens the rate to $$1/L$$. The two are combined with a maximum so that a leverage selection can never reserve less margin than the market minimum: a selection above $$L\_{max}$$ would imply a rate below the floor and is clamped back to $$r\_{im}$$ (validation rejects such values before storage; the clamp is defense in depth). An unset or zero leverage falls back to the market rate.

$$
r\_{eff} = \max!\left(\frac{1}{L},; r\_{im}\right) \tag{M.1}
$$

### Position margin requirements

The margin reserved to open exposure is linear in size, price, and rate. The rate is the market default $$r\_{im}$$ or, under per-position leverage, the effective rate from (M.1). The implementation computes the product exactly in decimal arithmetic and rounds the result away from zero at 28 decimal places, so the Exchange always requires at least the exact amount, never less.

$$
M\_{init} = q \cdot P \cdot r\_{im} \tag{M.2}
$$

Once open, a position must maintain a smaller cushion computed with the market's maintenance rate $$r\_{mm}$$. Because the implementation enforces $$r\_{mm} < r\_{im}$$ at parameter parse time, maintenance margin is strictly below initial margin for any positive size and price ((M.2)), giving every position a buffer between opening and liquidation thresholds. The same round-up (away from zero, 28 decimal places) applies.

$$
M\_{maint} = q \cdot P \cdot r\_{mm} \tag{M.3}
$$

### Pre-trade exposure

When an order of signed size $$\delta$$ arrives against a resting signed position $$s\_0$$ (positive long, negative short), the Exchange charges margin only for exposure the order *adds*. If the order flips the position through zero, the entire new side is fresh exposure; otherwise only the growth in position magnitude counts, clamped at zero so that pure reductions and closes add nothing. The result is always non-negative and is scaled by the initial margin rate via (M.2) to obtain the pre-trade margin charge; risk-reducing orders therefore charge zero, with the margin they free recognized after the fill rather than pre-credited.

$$
\Delta q = \begin{cases} |s\_0 + \delta| & \text{if } s\_0(s\_0+\delta) < 0 \quad \text{(flip)} \ \max!\big(|s\_0 + \delta| - |s\_0|,; 0\big) & \text{otherwise} \end{cases} \tag{M.4}
$$

### Account-level aggregates

Account equity is collateral plus the sum of unrealized profit and loss across all open positions. It is the account's liquidation-relevant net worth: gains on one position directly offset losses or margin requirements on another under cross margin.

$$
E = C + \Pi \tag{M.5}
$$

The margin ratio normalizes equity ((M.5)) by total position notional, where each position's notional is its size times the current mark price (falling back to entry price when no mark is available). It is undefined — the implementation returns no value — when the account has no open positions or when total notional is zero.

$$
\rho = \frac{E}{N} = \frac{C + \Pi}{\sum\_i q\_i , m\_i} \tag{M.6}
$$

Available margin is equity ((M.5)) minus the initial margin held for every existing position. A position stamped with an allocated margin at fill time (from the account's selected leverage) is held at exactly that amount; otherwise the market rate applies against the current mark per (M.2). The result can be negative, and can also exceed collateral when unrealized gains outweigh margin held — cross-margin offsetting is intentional.

$$
M\_{avail} = E - M\_{used} = (C + \Pi) - \sum\_i M\_{init,i} \tag{M.7}
$$

### Order admission

A new or increasing order is admitted when available margin ((M.7)) is at least the initial margin its quantity requires at the effective rate ((M.2)); equality passes — the comparison is greater-than-or-equal. Reduce-only orders skip the check entirely, since reducing a position releases margin rather than consuming it. On rejection the Exchange surfaces both the required and available amounts.

$$
H = M\_{avail} - q \cdot P \cdot r\_{im} ;; \geq ; 0 \iff \text{order admitted} \tag{M.8}
$$

Inverting (M.2) gives the largest position collateral can support at the mark price and market rate. The exact quotient is floored to an integer number of lots (truncation toward zero), so an account never receives a rounded-up fractional lot: sub-lot collateral yields a hard zero.

$$
q\_{max} = \left\lfloor \frac{C}{P \cdot r\_{im} \cdot \ell} \right\rfloor \cdot \ell \tag{M.9}
$$

### Portfolio aggregates

For cross-margined accounts, portfolio equity recomputes each position's unrealized PnL from fresh mark prices rather than trusting a cached value: signed size times mark-minus-entry, less the accumulated funding integral (positive $$\varphi$$ means funding paid, reducing equity). Isolated-mode positions are excluded — their margin is tracked per position. The formula below shows one position's contribution; the implementation sums over all cross positions.

$$
E\_{pf} = C + \sum\_i \Big( s\_i ,(m\_i - e\_i) - \varphi\_i \Big) \tag{M.10}
$$

The portfolio's initial margin sums (M.2) at the current mark across cross-mode positions. Positions stamped with an allocated margin contribute that amount instead of the market-rate computation, positions in markets with missing parameters are defensively skipped, and a missing mark price falls back to the entry price. The evaluable form shows one unstamped position's term.

$$
M\_{init}^{pf} = \sum\_i q\_i , m\_i , r\_{im,i} \tag{M.11}
$$

The maintenance analogue of (M.11): (M.3) summed across cross-mode positions at fresh marks, with the same entry-price fallback and missing-parameter skip. Because $$r\_{mm} < r\_{im}$$ per market, the portfolio maintenance total never exceeds the portfolio initial total on the same positions.

$$
M\_{maint}^{pf} = \sum\_i q\_i , m\_i , r\_{mm,i} \tag{M.12}
$$

## Invariants

* $$r\_{mm} < r\_{im}$$ for every market, enforced at parameter parse time; hence $$M\_{maint} < M\_{init}$$ ((M.3), (M.2)) for any positive size and price, and the same ordering holds for the portfolio sums.
* Required margin never rounds down: (M.2) and (M.3) round away from zero at 28 decimal places, so the stored requirement is always $$\geq$$ the exact real value.
* The effective initial margin rate is floored at the market rate: $$r\_{eff} \geq r\_{im}$$ ((M.1)), so per-position leverage can only tighten requirements.
* $$\Delta q \geq 0$$ always ((M.4)); pure reductions and exact closes yield exactly $$0$$, and a flip charges only the new side, never the full order size.
* Margin is monotone: $$M\_{init}$$ strictly increases in both size and price for fixed positive parameters.
* (M.9) always returns an integer multiple of the lot size, and collateral below one lot's requirement yields exactly zero — never a rounded-up position.
* Reduce-only orders unconditionally pass the margin check; the admission boundary in (M.8) is inclusive (available equal to required is admitted).
* Isolated-mode positions are excluded from every portfolio aggregate ((M.10), (M.11), (M.12)); their margin is checked per position.

## Worked example

Consider a BTC market with $$r\_{im} = 0.05$$ (20x maximum leverage), $$r\_{mm} = 0.025$$, and lot size $$\ell = 0.001$$, with the mark at $$P = 50{,}000$$. An account deposits $$C = 10{,}000$$ of collateral and submits a buy for $$q = 1$$ BTC with no leverage override. By (M.2) the order requires $$1 \times 50{,}000 \times 0.05 = 2{,}500$$ of initial margin. With no open positions, equity equals collateral ((M.5)), available margin is $$10{,}000$$ ((M.7)), and the headroom in (M.8) is $$10{,}000 - 2{,}500 = 7{,}500 \geq 0$$, so the order is admitted. Had the account instead selected 10x leverage, (M.1) gives $$r\_{eff} = \max(1/10, 0.05) = 0.10$$ and the requirement doubles to $$5{,}000$$.

After the fill the account is long $$s\_0 = +1$$ BTC at entry $$50{,}000$$. Its margin ratio ((M.6)) is $$\rho = 10{,}000 / 50{,}000 = 0.20$$, comfortably above the maintenance rate. The largest position this collateral could have supported is given by (M.9): the exact quotient $$10{,}000 / (50{,}000 \times 0.05) = 4$$ BTC is already a clean lot multiple, so $$q\_{max} = 4$$. With only $$2{,}750.50$$ of collateral the quotient would be $$1.1002$$ BTC, floored to $$1.100$$ — the fractional $$0.0002$$ above the lot grid is discarded, never rounded up.

Now suppose the account sells $$3$$ BTC against its $$+1$$ BTC position. By (M.4) the trade flips through zero: $$s\_0 + \delta = 1 - 3 = -2$$, so the added exposure is $$|{-2}| = 2$$ BTC — the new short side only, not the full 3 BTC order. The pre-trade charge is therefore $$2 \times 50{,}000 \times 0.05 = 5{,}000$$ via (M.2). A sell of at most $$1$$ BTC would have added zero exposure and, if flagged reduce-only, would bypass the margin check entirely.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression                      | Input                     | Elasticity ε |
| ------------------------------- | ------------------------- | ------------ |
| `effective_initial_margin_rate` | leverage                  | -1           |
| `effective_initial_margin_rate` | initial\_margin\_rate     | 0            |
| `initial_margin_required`       | size                      | 1            |
| `initial_margin_required`       | price                     | 1            |
| `initial_margin_required`       | initial\_margin\_rate     | 1            |
| `maintenance_margin_required`   | size                      | 1            |
| `maintenance_margin_required`   | price                     | 1            |
| `maintenance_margin_required`   | maintenance\_margin\_rate | 1            |
| `added_exposure`                | order\_signed             | 1.5          |
| `added_exposure`                | existing\_signed          | -0.5         |
| `equity`                        | total\_unrealized\_pnl    | 0.8          |
| `equity`                        | collateral                | 0.2          |
| `margin_ratio`                  | collateral                | 1            |
| `margin_ratio`                  | total\_notional           | -1           |
| `margin_ratio`                  | total\_unrealized\_pnl    | 0            |
| `available_margin`              | total\_unrealized\_pnl    | 1.053        |
| `available_margin`              | total\_initial\_margin    | -0.3158      |
| `available_margin`              | collateral                | 0.2632       |
| `order_margin_headroom`         | available\_margin         | 1.333        |
| `order_margin_headroom`         | size                      | -0.3333      |
| `order_margin_headroom`         | price                     | -0.3333      |
| `order_margin_headroom`         | initial\_margin\_rate     | -0.3333      |
| `max_position_size`             | collateral                | 125          |
| `max_position_size`             | price                     | -125         |
| `max_position_size`             | initial\_margin\_rate     | -6.25        |
| `max_position_size`             | lot\_size                 | 0            |
| `portfolio_equity`              | mark                      | 2.4          |
| `portfolio_equity`              | entry\_price              | -1.6         |
| `portfolio_equity`              | signed\_size              | 0.8          |
| `portfolio_equity`              | collateral                | 0.2          |
| `portfolio_equity`              | funding\_integral         | 0            |
| `portfolio_initial_margin`      | size                      | 1            |
| `portfolio_initial_margin`      | mark                      | 1            |
| `portfolio_initial_margin`      | initial\_margin\_rate     | 1            |
| `portfolio_maintenance_margin`  | size                      | 1            |
| `portfolio_maintenance_margin`  | mark                      | 1            |
| `portfolio_maintenance_margin`  | maintenance\_margin\_rate | 1            |

![Sensitivity tornado — Initial margin required](/files/JGGTcbOONtqaBNanIrmp)

![Sensitivity tornado — Added exposure](/files/O3GFF452mWU2tNjEXskQ)

![Sensitivity tornado — Maximum position size](/files/EYQi35NWa5QnEVfoYAVW)

### Response curves

![Required initial margin grows linearly in price, with lower leverage (a higher effective rate) reserving proportionally more; position size is held at 1 unit.](/files/g9PmW80VidySGXTa3q9l)

*Required initial margin grows linearly in price, with lower leverage (a higher effective rate) reserving proportionally more; position size is held at 1 unit.*

![Against a +1 long, sells up to the position size add zero exposure, larger sells charge only the new short side, and buys charge the full increment; the existing position is held at +1.](/files/bHAFtr24Rfu4TtX4rWnG)

*Against a +1 long, sells up to the position size add zero exposure, larger sells charge only the new short side, and buys charge the full increment; the existing position is held at +1.*

![Maximum openable size scales linearly with collateral but steps down to the lot grid, and collateral below one lot's requirement yields exactly zero; price and margin rate are held constant.](/files/779CEUviCPNA4iQqKL6R)

*Maximum openable size scales linearly with collateral but steps down to the lot grid, and collateral below one lot's requirement yields exactly zero; price and margin rate are held constant.*

### Parameter space

Joint parameter effects evaluated from the verified expressions over 2-D grids.

![The zero-headroom line available\_margin = size × 2,500 USDX separates accepted from rejected orders at 50,000 USDX price and 5% initial margin, with rejection depth growing linearly with size.](/files/nUSOT7J7KAT3ZK20ywyE)

*The zero-headroom line available\_margin = size × 2,500 USDX separates accepted from rejected orders at 50,000 USDX price and 5% initial margin, with rejection depth growing linearly with size.*

![Along the curve leverage = 1/initial\_margin\_rate the binding constraint switches from the trader's chosen leverage to the market floor, so higher leverage requests stop reducing margin once they cross it.](/files/apVJg8H4WdTyr72k48Q7)

*Along the curve leverage = 1/initial\_margin\_rate the binding constraint switches from the trader's chosen leverage to the market floor, so higher leverage requests stop reducing margin once they cross it.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [funding-rate](/math-engine/funding-rate)
* Sibling model: [insurance-fund](/math-engine/insurance-fund)
* Sibling model: [liquidation-engine](/math-engine/liquidation-engine)
* Sibling model: [oracle](/math-engine/oracle)
* Sibling model: [order-book](/math-engine/order-book)
* Sibling model: [position-tracker](/math-engine/position-tracker)
* Sibling model: [settlement](/math-engine/settlement)


# Liquidation Engine

When a leveraged account's equity falls to or below the maintenance margin required by its open positions, the Exchange liquidates: it generates reduce-only IOC orders that close positions at prices anchored to the account's bankruptcy point. This document derives the exact mathematics implemented by the liquidation engine. Equity is always recomputed from fresh mark prices $$m$$ rather than cached values, with a non-positive mark treated as missing and replaced by the entry price so an un-oracled market can never fabricate phantom losses. The trigger threshold is inclusive: an account exactly at maintenance liquidates. For cross-margin accounts underwater in several markets at once, the single collateral pool $$C$$ is allocated across markets in proportion to each market's loss, so the pool offsets the portfolio shortfall exactly once. All arithmetic is exact decimal arithmetic; rounding enters only where quantities are floored to the lot size and prices are aligned to the tick.

![One trigger inequality fans out into two independent per-position computations — a price (bankruptcy, tick-aligned) and a quantity (safe-size, partial-or-full) — that reconverge at the forced close, whose realized outcome feeds equity back into the trigger it came from.](/files/0yObb5920KoDR5ksHraJ)

*One trigger inequality fans out into two independent per-position computations — a price (bankruptcy, tick-aligned) and a quantity (safe-size, partial-or-full) — that reconverge at the forced close, whose realized outcome feeds equity back into the trigger it came from.*

## Setting

| Symbol          | Name                      | Description                                                                                                                                                  | Units                | Domain    |
| --------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | --------- |
| $$C$$           | account\_collateral       | Collateral backing the positions being evaluated: the account's shared pool for cross margin, or the position's allocated cushion for isolated margin.       | USDX                 | \[0, ∞)   |
| $$p\_e$$        | entry\_price              | Volume-weighted entry price of the position.                                                                                                                 | USDX per contract    | (0, ∞)    |
| $$m$$           | mark\_price               | Current oracle mark price. A non-positive stored mark means 'no oracle update yet' and is replaced by the entry price before any equity or loss computation. | USDX per contract    | (0, ∞)    |
| $$q$$           | size                      | Position size (always positive; direction is carried separately).                                                                                            | contracts            | (0, ∞)    |
| $$d$$           | direction                 | Position direction: $$+1$$ for long, $$-1$$ for short.                                                                                                       | dimensionless        | \[-1, 1]  |
| $$r\_m$$        | maintenance\_margin\_rate | Maintenance margin rate of the market (2.5% in the reference test parameters).                                                                               | fraction of notional | (0, 1]    |
| $$r\_i$$        | initial\_margin\_rate     | Initial margin rate of the market (5% in the reference test parameters).                                                                                     | fraction of notional | (0, 1]    |
| $$\delta$$      | tick\_size                | Minimum price increment of the market. A non-positive tick disables alignment entirely (the raw price passes through).                                       | USDX                 | (0, ∞)    |
| $$\lambda$$     | lot\_size                 | Minimum quantity increment of the market.                                                                                                                    | contracts            | (0, ∞)    |
| $$P$$           | price                     | A raw (unaligned) candidate limit price, e.g. an unrounded bankruptcy price, prior to tick alignment.                                                        | USDX                 | unbounded |
| $$\ell\_i$$     | loss\_i                   | This market's loss at mark: $$\max(0, -\text{uPnL}\_i)$$, using the entry-price fallback when the mark is unset.                                             | USDX                 | \[0, ∞)   |
| $$\mathcal{L}$$ | total\_loss               | Sum of losses across all positions being liquidated together.                                                                                                | USDX                 | (0, ∞)    |
| $$\pi$$         | pnl\_percent              | ADL candidate's unrealized PnL as a fraction of position value.                                                                                              | fraction             | unbounded |
| $$\Lambda$$     | leverage                  | ADL candidate's effective leverage.                                                                                                                          | dimensionless        | (0, ∞)    |

## The mechanism

### The Liquidation Trigger

Equity is never computed from a cached PnL field, which may be stale between mark updates. Instead the engine recomputes unrealized PnL directly from the current mark price. For a long ($$d=+1$$) the PnL is the mark's excess over entry scaled by size; for a short ($$d=-1$$) the sign flips. If the stored mark for a market is zero or negative — the stateless-boot sentinel for 'no oracle push yet' — the entry price is substituted, making the position's fresh PnL exactly zero rather than a catastrophic phantom loss.

$$
\text{uPnL} = d,(m - p\_e),q \tag{L.1}
$$

Equity is collateral plus the sum of fresh unrealized PnL ((L.1)) over all open positions. For a cross-margin account $$C$$ is the shared account collateral; for an isolated position $$C$$ is that position's own allocated margin ((L.5)) and the sum runs over that single position only.

$$
E = C + \sum\_{i} d\_i,(m\_i - p\_{e,i}),q\_i \tag{L.2}
$$

Each position requires maintenance margin proportional to its notional at the current mark, $$q,m,r\_m$$, computed by the margin primitive (see [(M.3)](/math-engine/margin-math) where published). The account-level requirement is the sum over positions, with the same entry-price fallback for unset marks.

$$
M = q,m,r\_m \tag{L.3}
$$

An account liquidates exactly when its equity ((L.2)) is at or below its total maintenance requirement ((L.3)). The threshold is inclusive: equity exactly equal to maintenance triggers liquidation. An account with no open positions never liquidates. The isolated variant is the single-position specialization with $$C$$ replaced by the position's allocated margin ((L.5)), evaluated against that position alone — sibling positions and account-level collateral never enter the isolated decision.

$$
\text{liquidate} \iff E \le \sum\_i M\_i \tag{L.4}
$$

### Isolated Positions

An isolated position is backed by the margin allocated to it at fill time. When no allocation was recorded, the engine falls back to the open-time initial margin at market rate — the same cushion the position margins against downstream. The trigger ((L.4)) and the liquidation pricing both use this single value, computed once per trigger, as the position's collateral.

$$
C\_{\text{iso}} = \begin{cases} A & \text{if allocated margin } A \text{ is recorded} \ q,p\_e,r\_i & \text{otherwise} \end{cases} \tag{L.5}
$$

### Cross-Margin Collateral Allocation

A cross-margin account holds one collateral pool backing positions in many markets. When several markets liquidate together, cushioning each market with the full pool would offset the portfolio's loss $$N$$ times, setting bankruptcy prices too far from entry and understating bad debt by $$(N-1),C$$. Instead each market receives a share proportional to its loss $$\ell\_i = \max(0, -\text{uPnL}\_i)$$; flat and winning positions receive nothing. If no position is at a loss (a degenerate case, since such a portfolio would not be liquidating) the pool splits evenly, $$C/N$$. The exact-decimal rounding remainder $$C - \sum\_i s\_i$$ is folded into the largest-loss position (ties break to the highest market identifier after sorting by market, making the placement replay-deterministic), so the shares always sum to exactly $$C$$. A set of zero or one positions receives the whole pool unchanged, so isolated and single-market paths are byte-identical to the pre-allocation behavior.

$$
s\_i = C,\frac{\ell\_i}{\mathcal{L}}, \qquad \mathcal{L} = \sum\_j \ell\_j \tag{L.6}
$$

### Liquidation Order Pricing

The bankruptcy price is the mark at which the position's loss exactly exhausts its collateral share: equity from this position alone reaches zero. For a long it sits below entry by $$C/q$$; for a short, above. This is the accounting anchor for the liquidation order's limit price. Note the caveat: liquidation orders are market-type IOC orders, so the book does not enforce this price as a matching bound — fills better than it produce insurance-fund spread profit, fills worse than it surface as bad debt.

$$
p\_b = p\_e - d,\frac{C}{q} \tag{L.7}
$$

Bankruptcy prices ((L.7)) are unrounded ratios, and the book rejects limits not aligned to the market tick. The engine rounds toward executability — a sell (closing a long) rounds down — conceding at most one tick beyond the bankruptcy bound. The result is clamped to at least one tick so the limit stays strictly positive; a non-positive tick size disables alignment and passes the raw price through.

$$
P^{\downarrow} = \max!\left(\left\lfloor \frac{P}{\delta} \right\rfloor \delta,; \delta\right) \tag{L.8}
$$

Symmetrically, a buy (closing a short) rounds up to the tick grid, so the worst-acceptable price stays acceptable. The same one-tick positivity clamp applies, though for a buy it can only bind when the raw price is non-positive.

$$
P^{\uparrow} = \max!\left(\left\lceil \frac{P}{\delta} \right\rceil \delta,; \delta\right) \tag{L.9}
$$

### Liquidation Sizing

The sizing rule is selected by the market's liquidation mode. In Full mode (the current default) the entire position closes. In Partial mode the engine computes the largest size the collateral can support at $$1.5\times$$ the initial margin rate — a buffer above initial margin so the surviving position is comfortably healthy — and floors it to the lot grid. Rounding is downward by design: rounding up would leave the position too large.

$$
q\_{\text{safe}} = \left\lfloor \frac{C}{1.5, m, r\_i,\lambda} \right\rfloor \lambda \tag{L.10}
$$

In Partial mode the order closes just the excess above the safe size ((L.10)); if the safe size already meets or exceeds the position, or the denominator is non-positive, the full position closes. In Full mode the quantity is simply $$q$$. A non-positive resulting quantity suppresses the order entirely.

$$
q\_{\text{liq}} = \begin{cases} q & q\_{\text{safe}} \ge q ;\text{or}; P,r\_i,1.5 \le 0 \ q - q\_{\text{safe}} & \text{otherwise} \end{cases} \tag{L.11}
$$

### Auto-Deleveraging

When liquidation cannot fully absorb a bankrupt position, profitable counterparties are auto-deleveraged in priority order. The score multiplies profitability by leverage, so the most profitable, most leveraged accounts are selected first. Candidates are ranked descending by score; exact ties break ascending on account identifier bytes, which makes the ordering fully deterministic under replay. The amount a counterparty can absorb in settlement is capped at its unrealized PnL — the profit it gives up to cover bad debt.

$$
S = \pi \cdot \Lambda \tag{L.12}
$$

## Invariants

* The trigger threshold is inclusive: $$E \le \sum\_i M\_i$$ liquidates, so an account exactly at maintenance is liquidated ((L.4)).
* An account with no open positions is never liquidated, and no liquidation order is ever generated against a market whose mark is unset (non-positive) — such marks fall back to the entry price for equity purposes and suppress order generation entirely.
* For a long, $$p\_b < p\_{\text{liq}} < m$$ at a healthy mark; for a short the inequalities reverse — the bankruptcy price always sits on the far side of the liquidation price ((L.7)).
* Cross-collateral shares conserve the pool exactly: $$\sum\_i s\_i = C$$, with the rounding remainder folded deterministically into the largest-loss market ((L.6)).
* Isolated liquidation depends only on the position's own allocated margin — account collateral and sibling positions never affect the isolated decision ((L.5)).
* Liquidation orders are always reduce-only, market-type, IOC, flagged as liquidations, sized at most the position size ($$q\_{\text{liq}} \le q$$), and sided opposite the position (long → sell, short → buy).
* Aligned limit prices are strictly positive and within one tick of the raw bankruptcy price, on the executable side: $$P - \delta < P^{\downarrow} \le P$$ for sells and $$P \le P^{\uparrow} < P + \delta$$ for buys, subject to the one-tick floor ((L.8), (L.9)).
* ADL ranking is a permutation of its input (no drops, no duplicates) and is deterministic across replays, via the descending-score, ascending-account-id ordering ((L.12)).

## Worked example

Consider a 1-contract long opened at $$p\_e = 50{,}000$$ with collateral $$C = 2{,}500$$ (5% initial margin), in a market with $$r\_m = 0.025$$, $$r\_i = 0.05$$, tick $$\delta = 0.5$$. At entry the position is healthy: equity is $$2{,}500$$ against maintenance of $$50{,}000 \times 0.025 = 1{,}250$$ ((L.3)). Now the mark drops to $$m = 48{,}000$$. Fresh PnL is $$(48{,}000 - 50{,}000) \times 1 = -2{,}000$$ ((L.1)), so equity is $$2{,}500 - 2{,}000 = 500$$ ((L.2)), while maintenance rises against the new notional to $$48{,}000 \times 0.025 = 1{,}200$$. Since $$500 \le 1{,}200$$, the trigger fires ((L.4)).

With a single position the collateral share is the whole pool ((L.6)), so the bankruptcy price is $$50{,}000 - 2{,}500 / 1 = 47{,}500$$ ((L.7)). That value is already a multiple of the $$0.5$$ tick, so sell-side alignment leaves it unchanged ((L.8)), and the engine emits a reduce-only IOC sell for the full 1 contract (Full mode) with limit price $$47{,}500$$.

In Partial mode with a larger book — a 5-contract long at the same entry, collateral $$12{,}500$$, mark at $$50{,}000$$ — the safe size is $$12{,}500 / (50{,}000 \times 0.05 \times 1.5) = 3.3\overline{3}$$, floored to the $$0.001$$ lot grid to $$3.333$$ contracts ((L.10)). The liquidation order therefore closes $$5 - 3.333 = 1.667$$ contracts ((L.11)), leaving the survivor margined at one and a half times the initial requirement.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression                 | Input                     | Elasticity ε |
| -------------------------- | ------------------------- | ------------ |
| `fresh_unrealized_pnl`     | entry\_price              | 25           |
| `fresh_unrealized_pnl`     | mark\_price               | -24          |
| `fresh_unrealized_pnl`     | direction                 | 1            |
| `fresh_unrealized_pnl`     | size                      | 1            |
| `account_equity`           | entry\_price              | -100         |
| `account_equity`           | mark\_price               | 96           |
| `account_equity`           | account\_collateral       | 5            |
| `account_equity`           | direction                 | -4           |
| `account_equity`           | size                      | -4           |
| `maintenance_margin`       | size                      | 1            |
| `maintenance_margin`       | mark\_price               | 1            |
| `maintenance_margin`       | maintenance\_margin\_rate | 1            |
| `liquidation_trigger`      | account\_collateral       | -5e+05       |
| `liquidation_trigger`      | direction                 | 5e+05        |
| `liquidation_trigger`      | mark\_price               | -5e+05       |
| `liquidation_trigger`      | entry\_price              | 5e+05        |
| `liquidation_trigger`      | size                      | 5e+05        |
| `liquidation_trigger`      | maintenance\_margin\_rate | 1.25e+04     |
| `isolated_margin_fallback` | size                      | 1            |
| `isolated_margin_fallback` | entry\_price              | 1            |
| `isolated_margin_fallback` | initial\_margin\_rate     | 1            |
| `cross_collateral_share`   | account\_collateral       | 1            |
| `cross_collateral_share`   | loss\_i                   | 1            |
| `cross_collateral_share`   | total\_loss               | -1           |
| `bankruptcy_price`         | entry\_price              | 1.053        |
| `bankruptcy_price`         | direction                 | -0.05263     |
| `bankruptcy_price`         | account\_collateral       | -0.05263     |
| `bankruptcy_price`         | size                      | 0.05263      |
| `aligned_price_sell`       | tick\_size                | 1            |
| `aligned_price_sell`       | price                     | 0            |
| `aligned_price_buy`        | tick\_size                | 1            |
| `aligned_price_buy`        | price                     | 0            |
| `safe_size`                | lot\_size                 | 0.09991      |
| `safe_size`                | account\_collateral       | 0            |
| `safe_size`                | mark\_price               | 0            |
| `safe_size`                | initial\_margin\_rate     | 0            |
| `partial_liquidation_qty`  | size                      | 2.999        |
| `partial_liquidation_qty`  | lot\_size                 | -0.1998      |
| `partial_liquidation_qty`  | account\_collateral       | 0            |
| `partial_liquidation_qty`  | mark\_price               | 0            |
| `partial_liquidation_qty`  | initial\_margin\_rate     | 0            |
| `adl_priority_score`       | pnl\_percent              | 1            |
| `adl_priority_score`       | leverage                  | 1            |

![Sensitivity tornado — Account equity](/files/txrGy0ONz2nNYFWHXBn3)

![Sensitivity tornado — Bankruptcy price](/files/JXS2fd5anwc3f3EAIHgY)

![Sensitivity tornado — Liquidation quantity](/files/859LtPb3MZ71oBO80hGh)

### Response curves

![Equity of a 1-contract long at a $50,000 entry as the mark falls, for three collateral levels; the shaded region marks where the thinnest account's equity is at or below maintenance margin (2.5% of notional at mark).](/files/axK2vp5JblOoOIgn1h8C)

*Equity of a 1-contract long at a $50,000 entry as the mark falls, for three collateral levels; the shaded region marks where the thinnest account's equity is at or below maintenance margin (2.5% of notional at mark).*

![The liquidation order's price anchor falls linearly away from the $50,000 entry as the collateral backing the position grows, with slope inversely proportional to position size.](/files/E6YwkPuJnhGXgDwIborR)

*The liquidation order's price anchor falls linearly away from the $50,000 entry as the collateral backing the position grows, with slope inversely proportional to position size.*

![How much of a 5-contract position Partial mode closes as collateral varies, holding the mark at 50,000; beyond 18,750 the safe size covers the whole position and the close quantity reaches zero.](/files/vscQHMO78IcPhOfa1fnb)

*How much of a 5-contract position Partial mode closes as collateral varies, holding the mark at* $$50,000; beyond$$*18,750 the safe size covers the whole position and the close quantity reaches zero.*

### Parameter space

Joint parameter effects evaluated from the verified expressions over 2-D grids.

![The liquidation boundary is a straight line in mark-collateral space whose slope reflects the size-weighted price sensitivity, holding a 1-contract long from 50,000 entry at a 2.5% maintenance rate.](/files/ZTqGJZSLq6RktmsYT1t6)

*The liquidation boundary is a straight line in mark-collateral space whose slope reflects the size-weighted price sensitivity, holding a 1-contract long from 50,000 entry at a 2.5% maintenance rate.*

![Higher maintenance rates pull the liquidation price toward entry, showing how risk-parameter tightening shrinks the safe zone for a 1-contract long with 2,500 USDX collateral.](/files/bDJS9BdxGWvRcu8dYqiE)

*Higher maintenance rates pull the liquidation price toward entry, showing how risk-parameter tightening shrinks the safe zone for a 1-contract long with 2,500 USDX collateral.*

![The liquidated quantity falls in lot-quantized steps as collateral grows, and the full-liquidation cliff shifts outward with mark price since the safe size scales inversely with notional, for a 5-contract position at 5% initial margin.](/files/YyiQCNPBcktuR9z2nwNN)

*The liquidated quantity falls in lot-quantized steps as collateral grows, and the full-liquidation cliff shifts outward with mark price since the safe size scales inversely with notional, for a 5-contract position at 5% initial margin.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [funding-rate](/math-engine/funding-rate)
* Sibling model: [insurance-fund](/math-engine/insurance-fund)
* Sibling model: [margin-math](/math-engine/margin-math)
* Sibling model: [oracle](/math-engine/oracle)
* Sibling model: [order-book](/math-engine/order-book)
* Sibling model: [position-tracker](/math-engine/position-tracker)
* Sibling model: [settlement](/math-engine/settlement)


# Oracle

Every perpetual market on the Exchange marks positions against a price that must be resistant to both fat-finger errors and deliberate manipulation. The oracle validator accepts a new print only if it stays within a per-step deviation bound of the current anchor $$P\_{oracle}$$ and, once ten prints have accumulated, within a wider path bound of the oldest print in the window. When the anchor is stale, a large move is never trusted on a single print: it must be confirmed by $$k$$ consecutive mutually-consistent prints, with a per-step bound that widens only after an escalation trigger fires.

The mark price $$P\_{mark}$$ that drives margin and liquidation is not the raw oracle price: it blends the trusted oracle anchor with a robust trade reference — the volume-weighted median of the last five trades — so that neither a single anomalous oracle tick nor a lone wash trade can move the mark across a liquidation threshold.

![Every print must clear both deviation bounds (a conjunctive fan-in on the anchor), while staleness opens a widening re-anchor loop whose confirmation threshold relaxes under escalation.](/files/6MYF6gqEszokSeBqgtBj)

*Every print must clear both deviation bounds (a conjunctive fan-in on the anchor), while staleness opens a widening re-anchor loop whose confirmation threshold relaxes under escalation.*

## Setting

| Symbol          | Name                       | Description                                                                                                                                                                                                                                                                                     | Units                  | Domain    |
| --------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | --------- |
| $$P\_{oracle}$$ | oracle\_price              | The current trusted oracle anchor price for the market. Strictly positive whenever the market has bootstrapped; a zero value denotes a brand-new market with no prior print.                                                                                                                    | USDX per unit of asset | (0, ∞)    |
| $$P\_{new}$$    | new\_price                 | The incoming oracle print being validated.                                                                                                                                                                                                                                                      | USDX per unit of asset | (0, ∞)    |
| $$P\_{old}$$    | oldest\_price              | The oldest accepted print in the rolling ten-entry update history, used by the path-manipulation check.                                                                                                                                                                                         | USDX per unit of asset | (0, ∞)    |
| $$\theta$$      | deviation\_threshold       | Per-market single-step deviation threshold (oracle\_deviation\_threshold); a print deviating from the anchor by strictly more than this is rejected.                                                                                                                                            | dimensionless fraction | (0, 1]    |
| $$N\_h$$        | history\_size              | Fixed size of the rolling price-update window used for path-manipulation detection (HISTORY\_SIZE = 10). The path check is armed only when the window is full.                                                                                                                                  | prints                 | \[10, 10] |
| $$P\_{trade}$$  | trade\_ref                 | Robust trade reference for the mark blend: the volume-weighted median price of the last five recorded trades, falling back to the last trade price when the window is empty. Weighting by traded size means moving the reference requires washing a majority of traded volume, not trade count. | USDX per unit of asset | (0, ∞)    |
| $$w$$           | oracle\_weight             | Per-market oracle weight in the mark blend (MarketParams.oracle\_mark\_weight), a unit-interval value defaulting to 0.95 (oracle-dominant).                                                                                                                                                     | dimensionless fraction | \[0, 1]   |
| $$P\_{mark}$$   | mark\_price                | The Exchange's mark price for the market: the oracle-weighted blend of the trusted anchor and the trade reference. Equals the oracle price when no trade has ever been recorded.                                                                                                                | USDX per unit of asset | (0, ∞)    |
| $$t\_{now}$$    | current\_time              | Timestamp of the incoming print or the staleness evaluation, in Unix milliseconds.                                                                                                                                                                                                              | milliseconds           | \[0, ∞)   |
| $$t\_{last}$$   | last\_update\_time         | Timestamp of the last trusted oracle update, in Unix milliseconds. Not advanced by pending (un-trusted) re-anchor prints.                                                                                                                                                                       | milliseconds           | \[0, ∞)   |
| $$\tau\_s$$     | staleness\_seconds         | Per-market staleness threshold (oracle\_staleness\_seconds); the anchor is stale when strictly more than this many seconds have elapsed since the last trusted update.                                                                                                                          | seconds                | (0, ∞)    |
| $$P\_{cand}$$   | candidate\_price           | The provisional re-anchor candidate: the running price level being confirmed while the anchor is stale. Not trusted and never used as the mark while pending.                                                                                                                                   | USDX per unit of asset | (0, ∞)    |
| $$\theta\_r$$   | reanchor\_max\_deviation   | Per-step consistency bound for re-anchor confirmations (oracle\_reanchor\_max\_deviation): a confirming print must land within this fraction of the running candidate or the confirmation counter restarts.                                                                                     | dimensionless fraction | (0, 1]    |
| $$\theta\_e$$   | escalation\_max\_deviation | Widened per-step bound (oracle\_reanchor\_escalation\_max\_deviation) applied once the escalation trigger has fired, letting a sustained-but-volatile legitimate correction accumulate confirmations. Finite: a step beyond it still restarts the counter.                                      | dimensionless fraction | (0, 1]    |
| $$k$$           | required\_confirmations    | Number of consecutive mutually-consistent prints required to trust a large move off a stale anchor (oracle\_reanchor\_confirmations). Floored at 2 in code so a misconfigured value of 1 cannot re-open the single-print bypass.                                                                | prints                 | \[2, ∞)   |
| $$n\_p$$        | pending\_prints            | Total prints observed during the current pending re-anchor sequence. Unlike the confirmation counter it never resets on an inconsistent step; it measures how long the market has been wedged.                                                                                                  | prints                 | \[0, ∞)   |
| $$N\_e$$        | escalation\_prints         | Print-count arm of the escalation trigger (oracle\_reanchor\_escalation\_prints).                                                                                                                                                                                                               | prints                 | \[1, ∞)   |
| $$t\_0$$        | pending\_since             | Timestamp (Unix milliseconds) of the print that opened the current pending re-anchor sequence; zero when not pending.                                                                                                                                                                           | milliseconds           | \[0, ∞)   |
| $$\tau\_e$$     | escalation\_seconds        | Wall-clock arm of the escalation trigger (oracle\_reanchor\_escalation\_seconds), measured against the feed-supplied update time.                                                                                                                                                               | seconds                | (0, ∞)    |

## The mechanism

### Deviation Guards

When the stored anchor is usable — positive and not stale — every incoming print is first measured against it as a relative deviation. The print is rejected when this deviation strictly exceeds the per-market threshold $$\theta$$ (the comparison is strict, so a move of exactly $$\theta$$ is accepted). This is the workhorse guard: a fat-finger print or a single-tick spike from the price producer is rejected here and never moves the anchor.

$$
\Delta\_{step} = \frac{\lvert P\_{new} - P\_{oracle} \rvert}{P\_{oracle}}, \qquad \text{accept only if } \Delta\_{step} \le \theta \tag{O.1}
$$

A manipulator who keeps each step under $$\theta$$ could still walk the price far over many prints. Once the rolling update history holds $$N\_h = 10$$ accepted prints, the validator also measures the incoming print against the oldest print in the window. This cumulative move is compared against the path threshold (O.3): a random walk over $$N$$ steps has expected deviation proportional to $$\sqrt{N}$$, while a directed walk grows proportionally to $$N$$, so scaling the threshold by $$\sqrt{N\_h}$$ separates the two regimes. The check is inactive while the window is refilling — after a re-anchor or on a brand-new market — a documented, accepted residual exposure.

$$
\Delta\_{path} = \frac{\lvert P\_{new} - P\_{old} \rvert}{P\_{old}} \tag{O.2}
$$

The path bound applied to (O.2) is the single-step threshold scaled by $$\sqrt{N\_h}$$. With the default $$\theta = 0.10$$ and $$N\_h = 10$$, the bound is $$0.10 \times \sqrt{10} \approx 0.3162$$: a cumulative move of more than about $$31.6%$$ across the window is rejected even though every individual step passed the single-step guard. The implementation uses the fixed decimal constant $$\sqrt{10} = 3.16227766016838$$.

$$
\theta\_{path} = \theta \cdot \sqrt{N\_h} \tag{O.3}
$$

### Staleness and Re-anchoring

The anchor is stale when strictly more than $$\tau\_s$$ seconds have elapsed since the last trusted update. Timestamps are Unix milliseconds, so the threshold is converted by multiplying by 1000 (with saturating integer arithmetic in the implementation). A stale anchor may hold an arbitrarily wrong value, so the deviation guards (O.1) and (O.2) cannot be trusted against it — staleness routes the print into the re-anchor path instead.

$$
\mathrm{stale} = \mathbb{1}\left\[, t\_{now} - t\_{last} > 1000,\tau\_s ,\right] \tag{O.4}
$$

A large move off a stale anchor — one whose deviation from the last-known-good price exceeds $$\theta$$ — is never trusted on a single print. It only seeds a candidate $$P\_{cand}$$; the mark does not move and the market stays fail-closed. Each subsequent print is measured against the running candidate: if the step stays within the active per-step bound (base $$\theta\_r$$, or the widened $$\theta\_e$$ once (O.6) fires) the confirmation counter advances; a larger step makes the print the fresh candidate with the counter restarted at 1. Only after $$k$$ consecutive consistent prints (with $$k$$ floored at 2) is the level trusted, the anchor moved, and the bookkeeping cleared. A benign recovery — a post-staleness print within $$\theta$$ of the old anchor — skips confirmation entirely and is trusted immediately.

$$
\Delta\_{cand} = \frac{\lvert P\_{new} - P\_{cand} \rvert}{P\_{cand}}, \qquad \text{confirm if } \Delta\_{cand} \le \theta\_{r}\ (\text{or } \theta\_{e} \text{ when escalated}) \tag{O.5}
$$

A legitimate correction whose true price moves more than $$\theta\_r$$ per print would reset the confirmation counter on every step and wedge the market fail-closed forever. The escalation trigger fires once the pending sequence has accumulated $$N\_e$$ prints or once $$\tau\_e$$ seconds have elapsed since it opened, whichever crosses first — the print counter $$n\_p$$ never resets on an inconsistent step, so it measures how long the market has been wedged. When active, the per-step bound in (O.5) widens from $$\theta\_r$$ to $$\theta\_e$$; no single print is ever trusted, and the widened bound remains finite.

$$
\mathrm{escalated} = \mathbb{1}\left\[, n\_p \ge N\_e ;\lor; t\_{now} - t\_0 \ge 1000,\tau\_e ,\right] \tag{O.6}
$$

### Mark Price

The mark price used for margin and liquidation blends the trusted oracle anchor with a robust trade reference. Because $$w \in \[0,1]$$, the mark always lies between the oracle price and the trade reference — an anomalous-but-accepted oracle tick is damped by trades, and vice versa. The trade reference $$P\_{trade}$$ is the volume-weighted median of the last five recorded trades: sorting the window by price and walking cumulative traded size, the reference is the first price at which cumulative size strictly exceeds half the total (when a sample's cumulative size lands exactly on half, the straddling price and the next positive-size price are averaged). Moving this reference therefore requires washing a majority of traded volume, not merely a majority of trade count. When the window is empty the reference falls back to the last trade price, and when no trade has ever been recorded the mark is simply the oracle price. A confirmed re-anchor over a large jump clears the trade window so pre-gap trades cannot pull the fresh mark back toward the stale level.

$$
P\_{mark} = w \cdot P\_{oracle} + (1 - w) \cdot P\_{trade} \tag{O.7}
$$

## Invariants

* The trusted anchor is strictly positive after bootstrap: $$P\_{oracle} > 0$$, and validation divides only by positive prices.
* On the fresh-anchor path, every accepted print satisfies $$\Delta\_{step} \le \theta$$ ((O.1)); the comparison is strict, so a move of exactly $$\theta$$ is accepted.
* When the ten-print history is full, every accepted print also satisfies $$\Delta\_{path} \le \theta \sqrt{10}$$ ((O.2), (O.3)).
* Because $$w \in \[0,1]$$, the mark is always bounded: $$\min(P\_{oracle}, P\_{trade}) \le P\_{mark} \le \max(P\_{oracle}, P\_{trade})$$ ((O.7)).
* A pending re-anchor never moves the anchor or advances the trusted timestamp: $$P\_{oracle}$$ and $$t\_{last}$$ are unchanged until $$k$$ consecutive consistent confirmations accumulate ((O.5)).
* The required confirmation count is floored at 2 in code, so a single print can never promote a large move off a stale anchor.
* A single trade with a minority of the window's traded volume cannot move the volume-weighted median trade reference, and hence cannot move the mark by more than the blend permits.
* Escalation widens the per-step bound to $$\theta\_e$$ but never removes it: a step beyond $$\theta\_e$$ still restarts the confirmation counter ((O.6)).

## Worked example

Consider a market with anchor $$P\_{oracle} = 100{,}000$$ USDX, deviation threshold $$\theta = 0.10$$, staleness window $$\tau\_s = 30$$ s, and oracle weight $$w = 0.95$$. A print of $$P\_{new} = 105{,}000$$ arrives one second after the last update. The anchor is fresh, so (O.1) gives $$\Delta\_{step} = |105{,}000 - 100{,}000| / 100{,}000 = 0.05 \le 0.10$$: the print is trusted and becomes the new anchor. Had the print been $$115{,}000$$, the deviation of $$0.15$$ would strictly exceed $$\theta$$ and the print would be rejected outright. Even a sequence of near-threshold steps is bounded: once ten prints fill the history, (O.2) is checked against (O.3) $$= 0.10 \times \sqrt{10} \approx 0.3162$$, so a compounding $$9%$$-per-print walk is cut off at the eleventh print.

Now suppose four trades of size 1 execute at $$99{,}000$$, $$100{,}000$$, $$101{,}000$$, and $$102{,}000$$. The equal-size volume-weighted median lands exactly on half the total volume between the two central prices, so the trade reference averages them: $$P\_{trade} = 100{,}500$$. By (O.7), $$P\_{mark} = 0.95 \times 100{,}000 + 0.05 \times 100{,}500 = 100{,}025$$ USDX. A lone wash trade at $$50{,}000$$ appended to a window of genuine $$100{,}000$$ trades leaves the median — and therefore the mark — unmoved, whereas the old single-last-trade blend would have dropped the mark to $$97{,}500$$.

Finally, suppose the feed goes silent for two minutes, so (O.4) fires ($$120{,}000 - 0 > 30 \times 1000$$), and the next print is $$42{,}000$$ against a stale anchor of $$160.165$$. The move is far beyond $$\theta$$, so it only seeds a candidate: the anchor and mark stay at the old level and the market remains fail-closed. Each subsequent print within $$\theta\_r = 0.10$$ of the running candidate ((O.5)) advances the confirmation counter; after $$k = 3$$ consecutive consistent prints the level is trusted, the anchor moves, and the stale pre-gap trade window is cleared so the fresh mark is the pure re-anchored oracle price.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression                | Input                | Elasticity ε |
| ------------------------- | -------------------- | ------------ |
| `single_step_deviation`   | new\_price           | 21           |
| `single_step_deviation`   | oracle\_price        | -21          |
| `path_deviation`          | new\_price           | 4.165        |
| `path_deviation`          | oldest\_price        | -4.165       |
| `path_threshold`          | deviation\_threshold | 1            |
| `is_stale`                | current\_time        | 0            |
| `is_stale`                | last\_update\_time   | 0            |
| `is_stale`                | staleness\_seconds   | 0            |
| `reanchor_step_deviation` | new\_price           | 1.111        |
| `reanchor_step_deviation` | candidate\_price     | -1.111       |
| `escalation_trigger`      | pending\_prints      | 5e+05        |
| `escalation_trigger`      | escalation\_prints   | -5e+05       |
| `escalation_trigger`      | current\_time        | 0            |
| `escalation_trigger`      | pending\_since       | 0            |
| `escalation_trigger`      | escalation\_seconds  | 0            |
| `mark_price`              | oracle\_price        | 0.9505       |
| `mark_price`              | trade\_ref           | 0.04952      |
| `mark_price`              | oracle\_weight       | 0.009505     |

![Sensitivity tornado — Single-step deviation](/files/xmZ0ew7yxv3TNmlSLImB)

![Sensitivity tornado — Mark price blend](/files/mTcGvoIiy4gM7IwADEtx)

### Response curves

![Relative deviation of an incoming print from a fixed 100,000 anchor; prints outside the ±10% band are rejected by the single-step guard.](/files/kZdFuKQS8BUOZCoT6Azf)

*Relative deviation of an incoming print from a fixed 100,000 anchor; prints outside the ±10% band are rejected by the single-step guard.*

![The mark blend as the trade reference varies, with the oracle anchor held at 100,000; higher oracle weights flatten the mark's sensitivity to trades.](/files/cjWCbRbEIsZ9dvzfP0JV)

*The mark blend as the trade reference varies, with the oracle anchor held at 100,000; higher oracle weights flatten the mark's sensitivity to trades.*

![The cumulative path bound is the single-step threshold scaled by √10 ≈ 3.162, holding the ten-print window size fixed.](/files/0097XQhRU1Wrhz0kHqWy)

*The cumulative path bound is the single-step threshold scaled by √10 ≈ 3.162, holding the ten-print window size fixed.*

### Parameter space

Joint parameter effects evaluated from the verified expressions over 2-D grids.

![The fresh/stale boundary is the line elapsed\_ms = 1000 × threshold, showing directly how tightening the staleness parameter shrinks the fresh region; last update time held at 0.](/files/0gWFhHLr6RHSJEk4KtQC)

*The fresh/stale boundary is the line elapsed\_ms = 1000 × threshold, showing directly how tightening the staleness parameter shrinks the fresh region; last update time held at 0.*

![Level sets fan out from the anchor point where trade\_ref equals the oracle price, showing that manipulation of the trade reference moves the mark by only (1 − weight) of the displacement; oracle price held at 100,000.](/files/YFHgNSsgri7x9p9hL8Rg)

*Level sets fan out from the anchor point where trade\_ref equals the oracle price, showing that manipulation of the trade reference moves the mark by only (1 − weight) of the displacement; oracle price held at 100,000.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [funding-rate](/math-engine/funding-rate)
* Sibling model: [insurance-fund](/math-engine/insurance-fund)
* Sibling model: [liquidation-engine](/math-engine/liquidation-engine)
* Sibling model: [margin-math](/math-engine/margin-math)
* Sibling model: [order-book](/math-engine/order-book)
* Sibling model: [position-tracker](/math-engine/position-tracker)
* Sibling model: [settlement](/math-engine/settlement)


# Order Book

Every market on the Exchange clears through one mechanism: a central limit order book. Traders who are willing to wait post limit orders that rest in the book, forming visible liquidity at discrete price levels; traders who want immediacy send orders that cross the spread and consume that liquidity. The book's contract is price-time priority — a better price always trades first, and at equal prices the earlier order trades first — which rewards the makers who quote tightest and earliest, and gives every taker the best available execution the book can offer.

Because makers commit first, every trade prints at the maker's quoted price: a taker willing to pay more than the best ask still pays only the ask. Around this core, the book layers execution guarantees that protect both sides — fill-or-kill orders that refuse partial execution, post-only orders that refuse to take liquidity, an opt-in self-trade prevention regime, and a server-enforced slippage cap that halts a market order the moment its running average price would drift too far from the mid-price it saw at submission.

![The fill event is a fan-in of resting depth, availability, and self-trade decrements, and each fill feeds back into both the depth it consumes and the running VWAP that bounds the next fill.](/files/naEFGv7c48sTUWiyqgCL)

*The fill event is a fan-in of resting depth, availability, and self-trade decrements, and each fill feeds back into both the depth it consumes and the running VWAP that bounds the next fill.*

## Setting

A market is described by parameters: the tick size $$\delta$$ (minimum price increment), the lot size $$\ell$$ (minimum quantity increment), and minimum and maximum order sizes. The state is a pair of price-level maps — bids and asks — each holding a first-in-first-out queue of resting orders per price, together with the derived best bid $$P\_b$$ and best ask $$P\_a$$. The admissible region for an incoming order requires a strictly positive, tick-aligned limit price and a lot-aligned quantity within the market's size bounds; orders outside it are rejected before touching the book.

| Symbol         | Name               | Description                                                                                                                                   | Units              | Domain  |
| -------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ------- |
| $$q$$          | quantity           | Order quantity in base units of the asset.                                                                                                    | base units         | (0, ∞)  |
| $$q\_f$$       | filled\_qty        | Cumulative quantity of the order already filled.                                                                                              | base units         | \[0, ∞) |
| $$\ell$$       | lot\_size          | Market lot size; order quantities must be integer multiples of it (a zero lot size disables the check).                                       | base units         | \[0, ∞) |
| $$\delta$$     | tick\_size         | Minimum price increment of the market. A non-positive tick disables alignment entirely (the raw price passes the check).                      | USDX per base unit | \[0, ∞) |
| $$P\_{lim}$$   | limit\_price       | Limit price of a resting or incoming limit order; must be strictly positive and tick-aligned.                                                 | USDX per base unit | (0, ∞)  |
| $$P\_b$$       | best\_bid          | Highest resting bid price; undefined when the bid side is empty.                                                                              | USDX per base unit | (0, ∞)  |
| $$P\_a$$       | best\_ask          | Lowest resting ask price; undefined when the ask side is empty.                                                                               | USDX per base unit | (0, ∞)  |
| $$P\_{mid}$$   | mid\_price         | Arithmetic midpoint of the best bid and best ask; the reference price captured once at market-order submission for the slippage cap.          | USDX per base unit | (0, ∞)  |
| $$\beta$$      | max\_slippage\_bps | Taker-supplied slippage cap on a market order, in basis points of the mid-price; absent means no cap.                                         | basis points       | \[0, ∞) |
| $$q\_t$$       | taker\_remaining   | Unfilled quantity remaining on the incoming (taker) order at the moment it meets a maker.                                                     | base units         | (0, ∞)  |
| $$q\_m$$       | maker\_remaining   | Unfilled quantity remaining on the resting (maker) order at the front of the queue.                                                           | base units         | (0, ∞)  |
| $$P\_{mk}$$    | maker\_price       | Limit price of the resting maker order; every fill prints at this price.                                                                      | USDX per base unit | (0, ∞)  |
| $$q^{\star}$$  | fill\_qty          | Quantity exchanged in a single fill between the taker and the front maker.                                                                    | base units         | (0, ∞)  |
| $$P^{\star}$$  | fill\_price        | Price of a single fill — always the maker's limit price.                                                                                      | USDX per base unit | (0, ∞)  |
| $$Q\_{avail}$$ | available\_qty     | Total resting quantity on the opposing side at prices satisfying the taker's limit — the pre-match liquidity visible to a fill-or-kill check. | base units         | \[0, ∞) |
| $$V\_k$$       | running\_notional  | Cumulative notional of the fills accepted so far in the current market-order walk.                                                            | USDX               | \[0, ∞) |
| $$Q\_k$$       | running\_filled    | Cumulative quantity of the fills accepted so far in the current market-order walk.                                                            | base units         | \[0, ∞) |
| $$V$$          | notional           | Total notional accumulated by a hypothetical walk of the opposing side in price-time priority.                                                | USDX               | (0, ∞)  |
| $$q\_{req}$$   | requested\_qty     | Quantity requested by a hypothetical market order in a VWAP preview.                                                                          | base units         | (0, ∞)  |

## The mechanism

### Admissibility

Before an order can touch the book, its quantity must be an exact multiple of the market's lot size $$\ell$$, and must lie within the market's minimum and maximum order sizes. Quantization keeps every resting queue and every fill expressible in whole lots, so partial fills never strand dust below the tradable increment. A zero lot size disables the multiplicity check.

$$
q \bmod \ell = 0 \tag{B.1}
$$

A limit price must be strictly positive and an exact multiple of the tick size $$\delta$$. Positivity is checked separately because zero is tick-aligned for every tick size — without the strict guard, a market booted before its first oracle print could accumulate zero-priced resting orders and print zero-priced trades. A non-positive tick size disables the alignment check but never the positivity one.

$$
P\_{lim} > 0 \quad\text{and}\quad P\_{lim} \bmod \delta = 0 \tag{B.2}
$$

### Book state

When both sides of the book are populated, the mid-price is the arithmetic midpoint of the best bid and best ask. It is the book's instantaneous consensus price and the reference captured once at submission for the market-order slippage cap (B.8). When either side is empty the mid-price is undefined, and a market order carrying a slippage cap is rejected outright rather than executed against an unpriced book.

$$
P\_{mid} = \frac{P\_b + P\_a}{2} \tag{B.3}
$$

### Matching

A fill-or-kill order executes only if the book can satisfy it entirely; otherwise it cancels without touching the book. Before matching, the engine sums the resting quantity $$Q\_{avail}$$ on the opposing side across every price level satisfying the taker's limit, and admits the order to the matching loop only when that sum covers the full request. A rejected fill-or-kill order records an expiry cancellation and generates no fills.

$$
Q\_{avail} \ge q \quad\text{where}\quad Q\_{avail} = \sum\_{\substack{\text{levels } P \text{ satisfying} \ \text{the taker limit}}} ; \sum\_{\text{orders at } P} (q - q\_f) \tag{B.4}
$$

The matching loop walks the opposing side of the book from the best price inward, and within each price level from the oldest order forward — price-time priority. Each encounter between the taker and the front maker exchanges exactly the smaller of the two remaining quantities: the maker cannot give more than it has resting, and the taker cannot take more than it still needs. A fully consumed maker leaves the book; a fully satisfied taker ends the walk.

$$
q^{\star} = \min\left(q\_t,; q\_m\right) \tag{B.5}
$$

Every fill prints at the maker's limit price, not the taker's. The maker committed capital first at a stated price, and the taker who crosses receives that price even when willing to trade at a worse one — the price improvement accrues entirely to the taker. Together with (B.5) this fully determines each fill: a buy taker therefore never pays above its limit, and a sell taker never receives below it.

$$
P^{\star} = P\_{mk} \tag{B.6}
$$

### Self-trade prevention

Self-trade prevention is opt-in and fires per encountered same-account maker, not once at order entry. In cancel-newest mode the taker cancels immediately; in cancel-oldest mode the maker cancels and the taker keeps walking. In decrement-and-cancel mode both orders shrink by the quantity they would have exchanged — the same minimum as (B.5) — but as a reduction of order size rather than a fill, so no trade is recorded. The smaller side is cancelled and the larger continues with reduced quantity; when both sides are equal, both cancel.

$$
\Delta\_{stp} = \min\left(q\_t,; q\_m\right) \tag{B.7}
$$

### Market-order slippage cap

A market order may carry a slippage cap $$\beta$$ in basis points. At submission the engine captures the mid-price (B.3) once and converts the cap into an absolute half-band around it; the running average execution price must remain within $$\[P\_{mid} - h,; P\_{mid} + h]$$ for the walk to continue. If either side of the book is empty at submission the mid-price does not exist and the capped order is rejected with insufficient liquidity rather than executed unbounded.

$$
h = P\_{mid} \cdot \frac{\beta}{10^4} \tag{B.8}
$$

As a capped market order walks the book, the engine evaluates, before accepting each candidate fill, the volume-weighted average price the order would have after that fill. If the prospective average would leave the band defined by (B.8), the fill is refused, the walk halts, and the remainder cancels with a slippage-cap reason — fills already accepted stand. The check is per-fill on the cumulative average, not per-level on the marginal price, so a deep sweep is halted exactly when its blended cost breaches the cap. The cap applies only to market orders; on limit orders it is silently ignored, since limit-or-better execution makes it meaningless.

$$
\bar{P}*{k+1} = \frac{V\_k + q^{\star} P^{\star}}{Q\_k + q^{\star}}, \qquad P*{mid} - h ;\le; \bar{P}*{k+1} ;\le; P*{mid} + h \tag{B.9}
$$

### Execution preview

The book also answers a read-only question: at what average price would a hypothetical market order of size $$q\_{req}$$ execute right now? The preview walks the opposing side in the same price-time order as live matching, accumulating notional $$V$$ until the request is covered, and returns the volume-weighted average. If resting liquidity cannot cover the request — or the requested quantity is non-positive — the preview returns no price rather than a partial estimate. Self-trade prevention and slippage caps are deliberately not applied here; they are concerns of the live submission path.

$$
\bar{P} = \frac{V}{q\_{req}}, \qquad V = \sum\_{\text{walked fills}} q^{\star} P^{\star} \tag{B.10}
$$

## Invariants

* The book is never crossed: whenever both sides are non-empty, $$P\_b < P\_a$$. *Why it holds:* An incoming limit order matches against every opposing level its price crosses before any remainder rests, so a resting bid at or above a resting ask cannot coexist — one would have consumed the other at submission. Post-only orders that would cross are rejected outright, and cancellations only remove orders, which cannot create a crossing.
* No overfill: for every order, cumulative filled quantity never exceeds order quantity, i.e. $$q\_f \le q$$; equivalently the sum of fill quantities equals at most the taker's quantity ((B.5)). *Why it holds:* Every fill quantity is $$\min(q\_t, q\_m)$$ of the two remaining quantities, so each fill reduces both remainders by a non-negative amount that cannot exceed either. The loop terminates the moment the taker's remainder reaches zero, so filled quantity is a monotone sum bounded by the original quantity.
* Limit-or-better execution: every fill of a buy taker satisfies $$P^{\star} \le P\_{lim}$$ and of a sell taker $$P^{\star} \ge P\_{lim}$$ ((B.6)). *Why it holds:* The matching loop selects a maker level only if it satisfies the taker's limit — asks at or below a buy limit, bids at or above a sell limit — and every fill prints at that maker price. Levels beyond the limit terminate the walk before generating fills.
* Index-book consistency: the order index contains exactly the identifiers of orders resting in the book, and every resting order has strictly positive remaining quantity. *Why it holds:* Every mutation path — insertion, cancellation, fill consumption, self-trade removal, and restore — updates the index and the price-level queues together in the same operation. Makers are removed from both the moment their remaining quantity reaches zero, and emptied price levels are deleted, so no zero-quantity order or dangling index entry survives any operation.
* Slippage-cap monotonicity: for the same book and order, a larger cap $$\beta$$ never fills less quantity than a smaller one ((B.8), (B.9)). *Why it holds:* The fills accepted before the first band breach are identical for both caps, since the walk order and the running VWAP sequence do not depend on the cap. A wider band can only move the first breach later in that fixed sequence, so the accepted prefix — and hence the filled quantity — is weakly larger. This is pinned by the property test p021\_slippage\_cap\_monotonic.

## Worked example

Consider a market with tick size $$\delta = 0.5$$ and lot size $$\ell = 0.001$$, and three resting asks from distinct accounts: 1 unit at $$100{,}000$$, 1 unit at $$101{,}000$$, and 1 unit at $$102{,}000$$. A market buy for $$2.5$$ units walks the levels in price priority. At the first level (B.5) gives $$q^{\star} = \min(2.5, 1) = 1$$ at fill price $$P^{\star} = 100{,}000$$ per (B.6); the second level fills another unit at $$101{,}000$$; at the third the taker's remainder is $$0.5$$, so $$q^{\star} = \min(0.5, 1) = 0.5$$ at $$102{,}000$$. The taker fully fills with notional $$V = 1 \cdot 100{,}000 + 1 \cdot 101{,}000 + 0.5 \cdot 102{,}000 = 252{,}000$$, an average price of $$252{,}000 / 2.5 = 100{,}800$$ per (B.10), and the third maker rests with $$0.5$$ units remaining.

Now add a slippage cap. With a bid resting at $$99{,}000$$ and the best ask at $$100{,}000$$, the submission-time mid-price is $$P\_{mid} = (99{,}000 + 100{,}000)/2 = 99{,}500$$ per (B.3). A market buy carrying $$\beta = 50$$ basis points gets a half-band $$h = 99{,}500 \cdot 50 / 10^4 = 497.5$$ per (B.8), so the running VWAP must stay at or below $$99{,}997.5$$. The very first candidate fill at $$100{,}000$$ would set the cumulative VWAP to $$100{,}000 > 99{,}997.5$$ per (B.9), so the fill is refused and the order cancels with a slippage-cap reason and zero fills — exactly the behavior pinned by the Rust test suite.

Finally, self-trade prevention in decrement-and-cancel mode: an account with $$1$$ unit resting sends a $$0.4$$-unit crossing order against itself. Per (B.7) both orders shrink by $$\Delta\_{stp} = \min(0.4, 1) = 0.4$$ with no fill recorded; the taker (the smaller side) cancels, and the maker continues resting with $$1 - 0.4 = 0.6$$ units.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression         | Input              | Elasticity ε |
| ------------------ | ------------------ | ------------ |
| `tick_alignment`   | limit\_price       | 0            |
| `tick_alignment`   | tick\_size         | 0            |
| `mid_price`        | best\_ask          | 0.5025       |
| `mid_price`        | best\_bid          | 0.4975       |
| `fok_availability` | available\_qty     | 0            |
| `fok_availability` | quantity           | 0            |
| `fill_quantity`    | taker\_remaining   | 1            |
| `fill_quantity`    | maker\_remaining   | 0            |
| `fill_price`       | maker\_price       | 1            |
| `stp_decrement`    | taker\_remaining   | 1            |
| `stp_decrement`    | maker\_remaining   | 0            |
| `slippage_span`    | mid\_price         | 1            |
| `slippage_span`    | max\_slippage\_bps | 1            |
| `running_vwap`     | running\_filled    | -0.8         |
| `running_vwap`     | running\_notional  | 0.7976       |
| `running_vwap`     | fill\_price        | 0.2024       |
| `running_vwap`     | fill\_qty          | 0.002381     |
| `vwap_estimate`    | notional           | 1            |
| `vwap_estimate`    | requested\_qty     | -1           |

![Sensitivity tornado — Fill quantity](/files/qDTRMyje3fsJvZfCzjpq)

![Sensitivity tornado — Slippage half-band](/files/mdvXDzmTx8lxoNnywEby)

![Sensitivity tornado — Running VWAP cap check](/files/fe0ySpWzIXQPgUww8dFK)

### Response curves

![The absolute price band a capped market order may traverse grows linearly in the basis-point cap, scaled by the submission-time mid-price held constant per series.](/files/6SRkzrCWnIcU1Nc9qpBY)

*The absolute price band a capped market order may traverse grows linearly in the basis-point cap, scaled by the submission-time mid-price held constant per series.*

![Each fill exchanges the minimum of the two remaining quantities: below the maker's size the taker binds, above it the maker binds (regions marked for the 1.0-unit maker).](/files/H1AjSOBVezm2gqWUpmlL)

*Each fill exchanges the minimum of the two remaining quantities: below the maker's size the taker binds, above it the maker binds (regions marked for the 1.0-unit maker).*

![With two units already filled at an average of 100,500, the cumulative VWAP climbs toward the third level's price as more of it is consumed; the worked example's 0.5-unit fill lands at 100,800.](/files/W3a1L107Kx4DYhNUVYqo)

*With two units already filled at an average of 100,500, the cumulative VWAP climbs toward the third level's price as more of it is consumed; the worked example's 0.5-unit fill lands at 100,800.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [funding-rate](/math-engine/funding-rate)
* Sibling model: [insurance-fund](/math-engine/insurance-fund)
* Sibling model: [liquidation-engine](/math-engine/liquidation-engine)
* Sibling model: [margin-math](/math-engine/margin-math)
* Sibling model: [oracle](/math-engine/oracle)
* Sibling model: [position-tracker](/math-engine/position-tracker)
* Sibling model: [settlement](/math-engine/settlement)


# Position Tracker

Every trade on the Exchange either opens, grows, shrinks, closes, or reverses a position, and the position tracker is the single bookkeeping authority for what that position is worth. Its central design choice is the split between money already banked and money still at risk: when a fill closes part of a position, the profit on the closed quantity is settled immediately at that fill's own price (realized PnL), while the remainder keeps floating with the mark price (unrealized PnL). The entry price is a volume-weighted average that only ever moves when the position grows — reducing a position never rewrites its history.

From the same state the tracker derives the two prices that govern an account's survival. The liquidation price is where the account's collateral plus floating PnL falls to exactly the maintenance-margin requirement — the point where the Exchange must step in. The bankruptcy price is more adverse still: it is where the collateral is fully consumed and equity reaches zero. The gap between the two is the buffer the liquidation engine has to unwind the position before losses spill beyond the account.

![Every fill branches on side into either an entry-price update or a realized-PnL close, and both risk prices are a fan-in of the same two state variables — entry price and collateral.](/files/82UtGCrGDedVQyeIXP0l)

*Every fill branches on side into either an entry-price update or a realized-PnL close, and both risk prices are a fan-in of the same two state variables — entry price and collateral.*

## Setting

A market is described by its parameters: the maintenance margin rate $$r\_m$$, the taker fee $$\beta\_t$$ and maker rebate $$\beta\_r$$ (both in basis points). A position carries a side $$\sigma \in {+1, -1}$$ (long, short), a positive size $$q$$, and a volume-weighted entry price $$P\_e > 0$$. Fills arrive with a price $$P\_f > 0$$ and quantity $$q\_f > 0$$, the market publishes a mark price $$P\_{mark} > 0$$, and the position is backed by account collateral $$C \geq 0$$. All quantities are exact decimals in the implementation; the formulas below are exact real arithmetic.

| Symbol        | Name                      | Description                                                                                                                 | Units                  | Domain   |
| ------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -------- |
| $$q$$         | size                      | Absolute position size in units of the underlying asset; direction is carried separately by the side.                       | units of asset         | (0, ∞)   |
| $$P\_e$$      | entry\_price              | Volume-weighted entry price of the position.                                                                                | USDX per base unit     | (0, ∞)   |
| $$P\_{mark}$$ | mark\_price               | The Exchange's mark price for the market at valuation time.                                                                 | USDX per unit of asset | (0, ∞)   |
| $$\sigma$$    | side\_sign                | Direction indicator for the position: $$+1$$ for a long position, $$-1$$ for a short position.                              | dimensionless          | \[-1, 1] |
| $$P\_f$$      | fill\_price               | Execution price of an individual fill applied to the position.                                                              | USDX per base unit     | (0, ∞)   |
| $$q\_f$$      | fill\_quantity            | Quantity of an individual fill applied to the position (always positive; direction comes from the fill side).               | base units             | (0, ∞)   |
| $$q\_c$$      | closed\_qty               | The portion of an opposing fill that closes existing position size: the smaller of the position size and the fill quantity. | base units             | (0, ∞)   |
| $$C$$         | account\_collateral       | Collateral backing the position being evaluated.                                                                            | USDX                   | \[0, ∞)  |
| $$r\_m$$      | maintenance\_margin\_rate | Market maintenance margin rate; strictly less than the initial margin rate.                                                 | dimensionless ratio    | (0, 1]   |
| $$\beta\_t$$  | taker\_fee\_bps           | Taker fee for the market, in basis points of fill notional.                                                                 | basis points           | \[0, ∞)  |
| $$\beta\_r$$  | maker\_rebate\_bps        | Maker rebate for the market, in basis points of fill notional; stored as a signed value whose magnitude is credited.        | basis points           | \[-∞, 0] |

## The mechanism

### Building the position

When a fill lands on the same side as an existing position, the position grows and its entry price becomes the volume-weighted average of the old position and the new fill: the new entry is the total cost of both legs divided by the total quantity. This is the only path on which the entry price changes — reductions, closes, and funding leave it untouched, so $$P\_e$$ always answers the question "what did the currently open size cost, on average?" A fill that opens a fresh position is the degenerate case with $$q = 0$$: the entry is simply the fill price.

$$
P\_e' = \frac{q , P\_e + q\_f , P\_f}{q + q\_f} \tag{T.1}
$$

### Valuing the position

The floating profit of the open position is the signed distance from entry to mark, scaled by size: a long gains when the mark rises above entry ($$\sigma = +1$$), a short when it falls below ($$\sigma = -1$$). This value is recomputed after every fill that leaves a position open, and again on every mark-price update; it is a pure revaluation and never moves collateral by itself.

$$
\text{uPnL} = \sigma ,(P\_{mark} - P\_e), q \tag{T.2}
$$

### Settling reductions

A fill on the opposing side first consumes existing position size. The quantity that closes is capped at the position size: a partial reduce closes the whole fill, a full close consumes exactly the position, and a flip closes the position and carries the excess into a new one. Only this closed portion settles PnL — the excess, if any, opens fresh exposure with no PnL of its own.

$$
q\_c = \min(q, , q\_f) \tag{T.3}
$$

Closing $$q\_c$$ units at the fill's own price banks the entry-to-fill difference, signed by the position side — the closing analogue of (T.2) with the fill price standing in for the mark. On a partial close this accumulates on the surviving position; on a full close or flip it is returned per fill so the risk layer can settle it into collateral, since the carried position (gone, or newly opened at (T.5)) cannot recover it. Fills that open or increase a position realize exactly zero.

$$
\text{rPnL} = \sigma ,(P\_f - P\_e), q\_c \tag{T.4}
$$

When an opposing fill exceeds the position size, the position reverses through zero: the old side closes fully (realizing (T.4) on $$q\_c = q$$), and the remainder opens a new position on the fill's side with the fill price as its fresh entry. The new position inherits none of the old accumulators.

$$
q\_{new} = q\_f - q \tag{T.5}
$$

### Fees and funding

Each taker fill charges a fee proportional to its notional value, at the market's taker rate in basis points. The tracker debits this amount from the position's fee PnL accumulator so the full P\&L breakdown (entry, funding, fee) is visible in one place; the cash movement itself is settled at the collateral layer. When a fill fully closes the position there is no surviving position to accumulate on, and the fee is settled entirely via collateral transfers.

$$
F\_t = \frac{q\_f , P\_f , \beta\_t}{10,000} \tag{T.6}
$$

Maker fills earn a rebate: the magnitude of the (negatively stored) maker rate, applied to the same fill notional as (T.6), is credited to the position's fee PnL. The absolute value guards against the sign convention — the stored rate is negative, but the credit is always positive.

$$
F\_m = \frac{q\_f , P\_f , |\beta\_r|}{10,000} \tag{T.7}
$$

### Risk prices

The liquidation price is the mark at which the account's equity — collateral plus the unrealized PnL of (T.2) evaluated at that mark — falls to exactly the maintenance-margin requirement $$P \cdot q \cdot r\_m$$. Solving $$C + \sigma(P - P\_e)q = P q r\_m$$ for $$P$$ gives a single closed form covering both sides: for a long ($$\sigma=+1$$) the price sits below entry, for a short ($$\sigma=-1$$) above it. The formula is meaningful for leveraged positions, where collateral is less than notional; a fully collateralized long yields a non-positive (unreachable) result.

$$
P\_{liq} = \frac{P\_e , q - \sigma, C}{q,(1 - \sigma, r\_m)} \tag{T.8}
$$

The bankruptcy price is where equity reaches zero: the entire collateral pool is consumed by the position's loss. Setting $$C + \sigma(P - P\_e)q = 0$$ and solving gives the entry price shifted adversely by the per-unit collateral $$C/q$$. Because the maintenance requirement in (T.8) is strictly positive, the bankruptcy price is always at least as adverse as the liquidation price — the gap is the liquidation engine's buffer to unwind the position before losses exceed the account.

$$
P\_{bkr} = P\_e - \sigma,\frac{C}{q} \tag{T.9}
$$

## Invariants

* Every open position has $$q > 0$$ and $$P\_e > 0$$. *Why it holds:* Opens and flips set size to a positive fill quantity (or positive excess $$q\_f - q$$) and entry to a positive fill price; increases add positive quantities; a reduction that would take size to zero returns no position at all rather than a zero-size one. So no path produces a live position with non-positive size or entry.
* Partial closes never change the entry price: after a reducing fill, $$P\_e' = P\_e$$ (see (T.1) for the only mutating path). *Why it holds:* The reduce branch subtracts from size and accumulates realized PnL but does not touch the entry field. Only the same-side (increase) branch recomputes the entry, and full closes and flips discard the old entry entirely.
* For leveraged positions ($$C < P\_e q$$), the bankruptcy price is at least as adverse as the liquidation price: $$\sigma P\_{bkr} \le \sigma P\_{liq}$$, i.e. $$P\_{bkr} \le P\_{liq}$$ for longs and $$P\_{bkr} \ge P\_{liq}$$ for shorts. *Why it holds:* At the liquidation price equity equals the maintenance requirement $$P\_{liq}, q, r\_m > 0$$; at the bankruptcy price equity is zero. Since equity is monotone in the mark (decreasing for longs, increasing for shorts), the zero-equity price lies strictly further in the adverse direction whenever the maintenance requirement is positive. Verified by the property test prop\_p005\_bankruptcy\_worse\_than\_liq over randomized sizes, entries, and collateral.
* A fill realizes PnL only on the quantity that closes existing opposite-side exposure: opens and increases return exactly zero realized PnL, and a flip realizes on $$q\_c = q$$, never on the newly opened $$q\_{new}$$ of (T.5). *Why it holds:* The open and increase branches return a zero realized amount unconditionally. The opposing-side branch computes (T.4) on $$\min(q, q\_f)$$ before any new position is constructed, so the reopened side starts with fresh, empty accumulators.
* Funding accrual is additive and orthogonal: applying a funding payment adds its amount to the funding accumulator and changes no other field — in particular fee PnL, entry, and size are preserved. *Why it holds:* apply\_funding performs a single addition to funding\_accrued and returns; the cash flow is handled at the collateral layer. Test f005\_funding\_tick\_preserves\_fee\_pnl confirms fee PnL survives funding ticks unchanged.

## Worked example

A trader opens a long with a buy of $$1$$ unit at $$P\_f = 45{,}000$$: the position is $$q = 1$$, $$P\_e = 45{,}000$$. A second buy of $$1$$ unit at $$47{,}000$$ increases the position, and (T.1) blends the two legs: $$P\_e' = (1 \cdot 45{,}000 + 1 \cdot 47{,}000)/2 = 46{,}000$$ on a size of $$q = 2$$. With the mark at $$P\_{mark} = 50{,}000$$, (T.2) values the position at $$\text{uPnL} = (50{,}000 - 46{,}000) \cdot 2 = 8{,}000$$ USDX of floating profit.

The trader now sells $$1$$ unit at $$50{,}000$$. The fill opposes the long, so (T.3) gives $$q\_c = \min(2, 1) = 1$$, and (T.4) banks $$(50{,}000 - 46{,}000) \cdot 1 = 4{,}000$$ USDX. The surviving position has $$q = 1$$ at the unchanged entry $$P\_e = 46{,}000$$. If instead the trader had sold $$3$$ units, the position would flip: $$q\_c = 2$$ closes the long (realizing $$8{,}000$$), and (T.5) opens a short of $$q\_{new} = 1$$ at entry $$50{,}000$$.

Suppose the surviving $$1$$-unit long is backed by $$C = 10{,}000$$ USDX in a market with $$r\_m = 0.005$$. (T.8) gives $$P\_{liq} = (46{,}000 - 10{,}000)/(1 \cdot 0.995) \approx 36{,}180.90$$, and (T.9) gives $$P\_{bkr} = 46{,}000 - 10{,}000 = 36{,}000$$. The mark reaches the liquidation threshold about $$181$$ USDX before equity would hit zero — that gap is the buffer within which the position must be unwound. Each taker fill along the way also cost (T.6): the opening $$45{,}000$$-notional buy, at $$\beta\_t = 5$$ bps, debited $$22.50$$ USDX of fee PnL.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression          | Input                     | Elasticity ε |
| ------------------- | ------------------------- | ------------ |
| `vwap_entry`        | fill\_price               | 0.5109       |
| `vwap_entry`        | entry\_price              | 0.4891       |
| `vwap_entry`        | size                      | -0.01087     |
| `vwap_entry`        | fill\_quantity            | 0.01087      |
| `unrealized_pnl`    | mark\_price               | 12.5         |
| `unrealized_pnl`    | entry\_price              | -11.5        |
| `unrealized_pnl`    | side\_sign                | 1            |
| `unrealized_pnl`    | size                      | 1            |
| `closed_quantity`   | fill\_quantity            | 1            |
| `closed_quantity`   | size                      | 0            |
| `realized_pnl`      | fill\_price               | 11           |
| `realized_pnl`      | entry\_price              | -10          |
| `realized_pnl`      | side\_sign                | 1            |
| `realized_pnl`      | closed\_qty               | 1            |
| `flip_size`         | fill\_quantity            | 2            |
| `flip_size`         | size                      | -1           |
| `taker_fee`         | fill\_quantity            | 1            |
| `taker_fee`         | fill\_price               | 1            |
| `taker_fee`         | taker\_fee\_bps           | 1            |
| `maker_rebate`      | fill\_quantity            | 1            |
| `maker_rebate`      | fill\_price               | 1            |
| `maker_rebate`      | maker\_rebate\_bps        | 1            |
| `liquidation_price` | entry\_price              | 1.111        |
| `liquidation_price` | size                      | 0.1111       |
| `liquidation_price` | account\_collateral       | -0.1111      |
| `liquidation_price` | side\_sign                | -0.1061      |
| `liquidation_price` | maintenance\_margin\_rate | 0.005025     |
| `bankruptcy_price`  | entry\_price              | 1.111        |
| `bankruptcy_price`  | side\_sign                | -0.1111      |
| `bankruptcy_price`  | account\_collateral       | -0.1111      |
| `bankruptcy_price`  | size                      | 0.1111       |

![Sensitivity tornado — Volume-weighted entry price](/files/pKmXIHjUrOiRFVoVWRLu)

![Sensitivity tornado — Unrealized PnL](/files/WhQ07h2LV9mf4WFpAORx)

![Sensitivity tornado — Liquidation price](/files/E13CF6IJgS0ez5eTAbcp)

### Response curves

![More collateral pushes a long's liquidation price further below the 100,000 entry, and larger positions dilute each collateral dollar across more units; maintenance rate held at 0.5%.](/files/sg3FfX8xpEjJgom9jlJW)

*More collateral pushes a long's liquidation price further below the 100,000 entry, and larger positions dilute each collateral dollar across more units; maintenance rate held at 0.5%.*

![Floating PnL is linear in the mark and mirror-imaged between long and short around the 46,000 entry, with size held at 2 units.](/files/hIiy6TnpAex2Mqy974jT)

*Floating PnL is linear in the mark and mirror-imaged between long and short around the 46,000 entry, with size held at 2 units.*

![The volume-weighted entry moves toward the fill price in proportion to the fill's share of the combined size; existing position held at 2 units entered at 46,000.](/files/1IiRwL3FlzoDNTZqy3Lc)

*The volume-weighted entry moves toward the fill price in proportion to the fill's share of the combined size; existing position held at 2 units entered at 46,000.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [funding-rate](/math-engine/funding-rate)
* Sibling model: [insurance-fund](/math-engine/insurance-fund)
* Sibling model: [liquidation-engine](/math-engine/liquidation-engine)
* Sibling model: [margin-math](/math-engine/margin-math)
* Sibling model: [oracle](/math-engine/oracle)
* Sibling model: [order-book](/math-engine/order-book)
* Sibling model: [settlement](/math-engine/settlement)


# Insurance Fund

When a position is liquidated at a price worse than its bankruptcy price, the account's collateral is insufficient to make its counterparties whole — the difference is bad debt, a loss that must land somewhere. The Exchange's first line of defense is the insurance fund: a pool of USDX that grows when liquidations execute at better-than-bankruptcy prices (spread profit) and shrinks when it absorbs bad debt. The mechanism is deliberately simple and total: every unit of bad debt is either absorbed by the fund or passed on, with nothing lost and nothing created.

The fund cannot go negative. If a liquidation's bad debt exceeds the current balance, the fund contributes everything it has — draining to exactly zero — and the remainder becomes an auto-deleveraging instruction: the Exchange forcibly closes the most profitable, most leveraged opposing positions to settle the shortfall. This document derives the exact arithmetic of that waterfall — absorption, depletion, the ADL handoff amount, the trigger condition, and the counterparty ranking — together with the accounting identities the implementation preserves.

![A single bad-debt event splits exactly into a min-term the fund absorbs and a max-term ADL settles (their sum is D by construction), while spread profits close the replenishment loop back into the balance that determines the next split.](/files/3OhGNAzRmqXtUktkUAsk)

*A single bad-debt event splits exactly into a min-term the fund absorbs and a max-term ADL settles (their sum is D by construction), while spread profits close the replenishment loop back into the balance that determines the next split.*

## Setting

A market is parameterized by an ADL trigger threshold $$\kappa \ge 0$$. The fund's state is the triple $$(F, \Sigma\_{\mathrm{abs}}, \Sigma\_{\mathrm{rec}})$$ of current balance, cumulative bad debt absorbed, and cumulative funds received; all three are non-negative USDX amounts, and a fresh fund starts at $$(F\_0, 0, F\_0)$$. Each liquidation presents a bad debt $$D \ge 0$$; each ADL candidate carries an unrealized-PnL fraction $$\pi$$ and leverage $$L$$ from which its priority score $$\rho$$ is formed. The admissible region is $$F \ge 0$$, $$D \ge 0$$, $$g \ge 0$$, $$\kappa \ge 0$$.

| Symbol                     | Name            | Description                                                                                                                                                                           | Units         | Domain    |
| -------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------- |
| $$\Phi$$                   | fund\_balance   | Current insurance fund balance available to absorb bad debt.                                                                                                                          | USDX          | \[0, ∞)   |
| $$D$$                      | bad\_debt       | Bad debt produced by a liquidation: the shortfall between the bankrupt account's obligations and its collateral. Zero when the liquidation closed at or better than bankruptcy price. | USDX          | \[0, ∞)   |
| $$\Sigma\_{\mathrm{abs}}$$ | total\_absorbed | Cumulative bad debt the fund has absorbed over its lifetime; monotonically non-decreasing.                                                                                            | USDX          | \[0, ∞)   |
| $$\Sigma\_{\mathrm{rec}}$$ | total\_received | Cumulative funds the fund has received — the initial balance plus all spread profits; monotonically non-decreasing.                                                                   | USDX          | \[0, ∞)   |
| $$g$$                      | spread\_profit  | Liquidation spread profit credited to the fund when a liquidation fills at a better-than-bankruptcy price.                                                                            | USDX          | \[0, ∞)   |
| $$\kappa$$                 | adl\_threshold  | Per-market ADL trigger threshold on the fund balance. The default of zero means ADL only triggers when the fund is fully depleted; a positive value triggers ADL earlier.             | USDX          | \[0, ∞)   |
| $$\pi$$                    | pnl\_percent    | ADL candidate's unrealized PnL as a fraction of position value.                                                                                                                       | fraction      | unbounded |
| $$L$$                      | leverage        | ADL candidate's effective leverage.                                                                                                                                                   | multiplier    | (0, ∞)    |
| $$\rho$$                   | priority\_score | ADL ranking score for a counterparty candidate; candidates are deleveraged in descending order of this score.                                                                         | dimensionless | unbounded |

## The mechanism

### Bad-Debt Absorption

A liquidation presents bad debt $$D$$ to the fund, and the fund contributes as much as it can without going negative: the full debt when it is covered, and the entire balance otherwise. This is the amount by which the cumulative absorption ledger $$\Sigma\_{\mathrm{abs}}$$ advances, and its complement — (I.4) — is what escapes to auto-deleveraging. The case $$D = 0$$ is included: the fund absorbs nothing and no ADL is produced.

$$
D\_{\mathrm{abs}} = \min(D, \Phi) \tag{I.1}
$$

The balance after a liquidation is the previous balance less the absorbed amount (I.1), which collapses to a clamped subtraction: the fund keeps whatever the debt did not consume, and never dips below zero. When $$D > F$$ the two branches of the implementation meet exactly at zero — the fund is drained with no residual dust and no overdraw.

$$
\Phi' = \max(\Phi - D, 0) \tag{I.2}
$$

Every liquidation advances the lifetime absorption ledger by exactly the absorbed amount (I.1), never by the full bad debt when the fund cannot cover it. This keeps the ledger an honest record of what the fund itself paid, which is what makes the accounting-closure invariant hold.

$$
\Sigma\_{\mathrm{abs}}' = \Sigma\_{\mathrm{abs}} + \min(D, \Phi) \tag{I.3}
$$

### Auto-Deleveraging Handoff

When bad debt exceeds the fund balance, the excess is packaged into an ADL instruction whose settlement amount is exactly the shortfall the fund could not cover. Together with (I.1) this conserves the debt: $$D\_{\mathrm{abs}} + D\_{\mathrm{adl}} = D$$ always. No instruction is issued when $$D \le F$$ — the expression evaluates to zero there, and the implementation returns no ADL at all.

$$
D\_{\mathrm{adl}} = \max(D - \Phi, 0) \tag{I.4}
$$

Independently of any single liquidation, the Exchange can consult a market-level trigger: auto-deleveraging is armed whenever the fund balance has fallen to or below the market's threshold $$\kappa$$. With the default $$\kappa = 0$$ this arms only when the fund is fully depleted; a positive threshold arms ADL earlier as a safety margin. The comparison is inclusive — balance exactly at the threshold triggers.

$$
\mathrm{trigger}\_{\mathrm{adl}} = \mathbb{1}\left\[\Phi \le \kappa\right] \tag{I.5}
$$

The ADL instruction lists counterparties in the order they will be deleveraged: descending by priority score, the product of a candidate's unrealized-PnL fraction $$\pi$$ and its leverage $$L$$. The most profitable, most leveraged opposing positions absorb the shortfall first — the accounts that gained most from the move that bankrupted the liquidated trader. Ties on score are broken by ascending account identifier for deterministic replay.

$$
\rho = \pi \cdot L \tag{I.6}
$$

### Spread-Profit Crediting

The fund's only inflow after inception is liquidation spread profit: when a liquidation fills at a better-than-bankruptcy price, the surplus $$g$$ is credited to the fund. The balance increases by exactly the profit — no fee, no haircut — and $$g = 0$$ leaves the state unchanged.

$$
\Phi' = \Phi + g \tag{I.7}
$$

Each spread-profit credit also advances the lifetime receipts ledger, which was seeded with the fund's initial balance at construction. Liquidations never touch this ledger — bad debt moves value from $$F$$ to $$\Sigma\_{\mathrm{abs}}$$, not out of $$\Sigma\_{\mathrm{rec}}$$ — so receipts are monotonically non-decreasing and the closure identity with (I.7) and (I.3) is preserved on every path.

$$
\Sigma\_{\mathrm{rec}}' = \Sigma\_{\mathrm{rec}} + g \tag{I.8}
$$

## Invariants

* The fund balance is never negative: $$F \ge 0$$ after every operation, and it is exactly zero whenever an ADL instruction is emitted. *Why it holds:* Absorption subtracts $$D$$ only on the branch $$D \le F$$, so the result is non-negative; on the other branch the balance is assigned literally to zero after contributing everything ((I.2)). Spread-profit crediting only adds a non-negative amount. No other path writes the balance.
* Accounting closure: $$F = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}}$$ at all times, equivalently $$F + \Sigma\_{\mathrm{abs}} = \Sigma\_{\mathrm{rec}}$$. *Why it holds:* The initial state $$(F\_0, 0, F\_0)$$ satisfies it. Absorption moves the same amount $$\min(D,F)$$ out of $$\Phi$$ and into $$\Sigma\_{\mathrm{abs}}$$ ((I.1), (I.3)), leaving the difference unchanged; spread profit adds the same $$g$$ to both $$\Phi$$ and $$\Sigma\_{\mathrm{rec}}$$ ((I.7), (I.8)).
* Bad-debt conservation: every liquidation's debt splits exactly as $$D = D\_{\mathrm{abs}} + D\_{\mathrm{adl}}$$ with $$D\_{\mathrm{abs}} = \min(D,F)$$ and $$D\_{\mathrm{adl}} = \max(D-F,0)$$ — nothing is lost or double-counted between the fund and ADL. *Why it holds:* The identity $$\min(D,F) + \max(D-F,0) = D$$ holds pointwise. The covered branch sets $$D\_{\mathrm{adl}} = 0$$ implicitly (no instruction); the depleted branch computes the ADL amount as $$D - F$$ ((I.4)) after absorbing exactly $$\Phi$$.
* Both ledgers are monotone: $$\Sigma\_{\mathrm{abs}}$$ and $$\Sigma\_{\mathrm{rec}}$$ never decrease, and liquidations leave $$\Sigma\_{\mathrm{rec}}$$ untouched. *Why it holds:* Absorption adds a non-negative $$\min(D,F)$$ to $$\Sigma\_{\mathrm{abs}}$$ and never writes $$\Sigma\_{\mathrm{rec}}$$; spread profit adds a non-negative $$g$$ to $$\Sigma\_{\mathrm{rec}}$$ and never writes $$\Sigma\_{\mathrm{abs}}$$. ADL counterparty settlements happen outside the fund's books entirely.

## Worked example

Start a market's fund at $$F\_0 = 1{,}000$$ USDX, so the state is $$(F, \Sigma\_{\mathrm{abs}}, \Sigma\_{\mathrm{rec}}) = (1000, 0, 1000)$$. A first liquidation fills slightly better than bankruptcy and yields a spread profit of $$g = 250$$: by (I.7) and (I.8) the state becomes $$(1250, 0, 1250)$$, and closure $$F = \Sigma\_{\mathrm{rec}} - \Sigma\_{\mathrm{abs}}$$ holds.

A second liquidation leaves bad debt $$D = 1{,}500$$. Since $$D > F$$, the fund contributes everything: (I.1) gives $$D\_{\mathrm{abs}} = \min(1500, 1250) = 1250$$, the balance drops to $$\max(1250 - 1500, 0) = 0$$ by (I.2), and $$\Sigma\_{\mathrm{abs}}$$ advances to $$1250$$. The shortfall becomes an ADL instruction for $$D\_{\mathrm{adl}} = \max(1500 - 1250, 0) = 250$$ by (I.4). With the default threshold $$\kappa = 0$$, the trigger (I.5) now reads $$\mathbb{1}\[0 \le 0] = 1$$ — ADL is armed.

The instruction ranks counterparties by (I.6). Consider three profitable longs with $$(\pi, L)$$ of $$(0.02, 10)$$, $$(0.1, 15)$$, and $$(0.04, 10)$$: their scores are $$0.2$$, $$1.5$$, and $$0.4$$, so the second account — the most profitable and most leveraged — is deleveraged first to settle the $$250$$ USDX shortfall. Throughout, the final state $$(0, 1250, 1250)$$ still satisfies closure exactly.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression                 | Input           | Elasticity ε |
| -------------------------- | --------------- | ------------ |
| `absorbed_amount`          | bad\_debt       | 1            |
| `absorbed_amount`          | fund\_balance   | 0            |
| `post_liquidation_balance` | fund\_balance   | 2            |
| `post_liquidation_balance` | bad\_debt       | -1           |
| `total_absorbed_update`    | bad\_debt       | 1            |
| `total_absorbed_update`    | total\_absorbed | 0            |
| `total_absorbed_update`    | fund\_balance   | 0            |
| `adl_settle_amount`        | bad\_debt       | 3            |
| `adl_settle_amount`        | fund\_balance   | -2           |
| `adl_priority_score`       | pnl\_percent    | 1            |
| `adl_priority_score`       | leverage        | 1            |
| `spread_profit_balance`    | fund\_balance   | 0.9756       |
| `spread_profit_balance`    | spread\_profit  | 0.02439      |
| `total_received_update`    | total\_received | 0.9756       |
| `total_received_update`    | spread\_profit  | 0.02439      |

![Sensitivity tornado — Fund balance after absorption](/files/jBRBlSFQ0PAjm0rAjsMv)

![Sensitivity tornado — ADL settlement amount](/files/ZdK3aVGaoRCarmm4FTFZ)

![Sensitivity tornado — ADL counterparty priority score](/files/xeHFeI7AIjoSoEuAPgcR)

### Response curves

![The balance falls one-for-one with bad debt until it clamps at exactly zero, where auto-deleveraging takes over; initial balance held at 10,000 USDX.](/files/7D3VYG7q7VQZvlKVKKor)

*The balance falls one-for-one with bad debt until it clamps at exactly zero, where auto-deleveraging takes over; initial balance held at 10,000 USDX.*

![The ADL settlement amount is zero while the fund covers the debt and grows one-for-one with the excess beyond the fund balance, shown for three fund sizes.](/files/AERStG69hKFHK6WnFT41)

*The ADL settlement amount is zero while the fund covers the debt and grows one-for-one with the excess beyond the fund balance, shown for three fund sizes.*

![Counterparties are deleveraged in descending score order, so more profitable and more leveraged positions absorb the shortfall first; three leverage levels shown.](/files/vaMWa2jCdlj1UE3Zew0Z)

*Counterparties are deleveraged in descending score order, so more profitable and more leveraged positions absorb the shortfall first; three leverage levels shown.*

### Parameter space

Joint parameter effects evaluated from the verified expressions over 2-D grids.

![The fund fully absorbs losses below the diagonal bad\_debt = fund\_balance; above it, the excess passes linearly to ADL counterparties, making the diagonal the socialization boundary.](/files/ziN2o3kvIIbU8xqwYkt8)

*The fund fully absorbs losses below the diagonal bad\_debt = fund\_balance; above it, the excess passes linearly to ADL counterparties, making the diagonal the socialization boundary.*

![Priority hyperbolas show that a modestly profitable high-leverage position outranks a highly profitable low-leverage one, while loss-making positions (negative scores) are deprioritized regardless of leverage.](/files/CS2T68msYstIM1YaXDYO)

*Priority hyperbolas show that a modestly profitable high-leverage position outranks a highly profitable low-leverage one, while loss-making positions (negative scores) are deprioritized regardless of leverage.*

![ADL arms exactly on and below the diagonal fund\_balance = adl\_threshold, showing how the threshold parameter directly sets the fund depletion level at which deleveraging begins.](/files/hOaz60ayMjFXRJLuUcZj)

*ADL arms exactly on and below the diagonal fund\_balance = adl\_threshold, showing how the threshold parameter directly sets the fund depletion level at which deleveraging begins.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [funding-rate](/math-engine/funding-rate)
* Sibling model: [liquidation-engine](/math-engine/liquidation-engine)
* Sibling model: [margin-math](/math-engine/margin-math)
* Sibling model: [oracle](/math-engine/oracle)
* Sibling model: [order-book](/math-engine/order-book)
* Sibling model: [position-tracker](/math-engine/position-tracker)
* Sibling model: [settlement](/math-engine/settlement)


# Settlement

Every trade on the Exchange ends in settlement: the moment abstract execution becomes actual transfers of USDX between accounts. The mechanism exists to make the Exchange's fee economics explicit and auditable — the taker, who consumes liquidity, pays a small fee on the trade's notional value; the maker, who provided the resting quote, receives a rebate funded out of that fee; and the difference is the Exchange's revenue on the fill. When the fill is a forced liquidation, an additional penalty on the same notional flows to the insurance fund, compensating the system for absorbing distressed risk.

Funding settlement is the second half of the story. Perpetual markets periodically charge one side of the market and pay the other to keep the contract price tethered to the index; settlement realizes those charges as transfers through a dedicated funding pool account. Throughout, the design commitment is conservation: every fee, rebate, penalty, and funding payment is a transfer between two named accounts, so money is moved, never created or destroyed.

![Each fill fans its notional out into fee, rebate, and penalty flows that recombine into ledger totals whose batch merge closes with V = F − R exactly.](/files/l6dGf425NiAtOD70buh1)

*Each fill fans its notional out into fee, rebate, and penalty flows that recombine into ledger totals whose batch merge closes with V = F − R exactly.*

## Setting

A market is parameterized by three fee rates quoted in basis points: the taker fee $$b\_t$$, the maker rebate $$b\_m$$ (stored as a negative number by convention; only its magnitude is charged), and the liquidation penalty $$b\_{liq}$$. The state being settled is a batch of fills, each with size $$q > 0$$ and price $$P > 0$$, plus a batch of signed funding payment amounts $$x$$. Three reserved accounts — the Exchange fee account, the insurance fund, and the funding pool — act as counterparties with untracked balances. All monetary results live in USDX and are truncated toward zero at 28 decimal places.

| Symbol       | Name                      | Description                                                                                                                                                           | Units              | Domain    |
| ------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | --------- |
| $$q$$        | size                      | Fill quantity in units of the underlying asset; always positive for an executed fill.                                                                                 | units of asset     | (0, ∞)    |
| $$P$$        | price                     | Execution price of the fill.                                                                                                                                          | USDX per base unit | (0, ∞)    |
| $$b\_t$$     | taker\_fee\_bps           | Market taker fee rate in basis points; divided by 10,000 to obtain the fee fraction.                                                                                  | basis points       | \[0, ∞)   |
| $$b\_m$$     | maker\_rebate\_bps        | Market maker rebate rate in basis points, stored as a negative number by convention; the settlement engine charges only its absolute value.                           | basis points       | unbounded |
| $$b\_{liq}$$ | liquidation\_penalty\_bps | Penalty rate in basis points applied to the notional of liquidation fills and routed to the insurance fund.                                                           | basis points       | \[0, ∞)   |
| $$F$$        | total\_fees               | Sum of taker fees collected across all fills in a settlement record.                                                                                                  | USDX               | \[0, ∞)   |
| $$R$$        | total\_rebates            | Sum of maker rebates paid across all fills in a settlement record.                                                                                                    | USDX               | \[0, ∞)   |
| $$x$$        | funding\_amount           | Signed funding payment amount for one account: positive means the account pays the funding pool, negative means the pool pays the account, zero produces no transfer. | USDX               | unbounded |
| $$V\_1$$     | volume\_1                 | An accumulator field of one constituent settlement record being merged (shown for volume; the same summation applies to fees, rebates, and net revenue).              | USDX               | \[0, ∞)   |
| $$V\_2$$     | volume\_2                 | An accumulator field of one constituent settlement record being merged (shown for volume; the same summation applies to fees, rebates, and net revenue).              | USDX               | \[0, ∞)   |

## The mechanism

### Fee Settlement

Every fee in this primitive is proportional to the same base quantity: the notional value of the fill, the size traded times the price it traded at. Computing it once per fill fixes the base on which the taker fee, maker rebate, and liquidation penalty are all assessed.

$$
n = q \cdot P \tag{S.1}
$$

The taker — the aggressing side that consumed resting liquidity — pays the Exchange a fee proportional to the fill's notional (S.1). The rate is the market's taker fee expressed in basis points, so a market with $$b\_t = 5$$ charges five hundredths of a percent of notional. The resulting amount moves from the taker's account to the Exchange fee account. The product is truncated toward zero at 28 decimal places, so the fee never rounds up against the taker.

$$
\text{fee}\_t = n \cdot \frac{b\_t}{10,000} = q \cdot P \cdot \frac{b\_t}{10,000} \tag{S.2}
$$

The maker, whose resting order provided the liquidity, is paid a rebate out of the Exchange fee account, again proportional to the notional (S.1). Market parameters store the maker rate as a negative number of basis points to signal that money flows toward the maker; the engine applies its absolute value, so the transferred amount is always non-negative. Like the taker fee (S.2), the amount is truncated toward zero at 28 decimal places.

$$
\text{rebate}\_m = n \cdot \frac{|b\_m|}{10,000} = q \cdot P \cdot \frac{|b\_m|}{10,000} \tag{S.3}
$$

When a fill is flagged as a liquidation — the taker side is a forced close of a distressed position — an additional penalty is assessed on the same notional (S.1) at the market's liquidation penalty rate. This transfer runs from the taker's account to the insurance fund, not to the Exchange fee account: the penalty capitalizes the backstop that absorbs losses when liquidations complete underwater. Non-liquidation fills incur no penalty, and the amount is truncated toward zero at 28 decimal places.

$$
\text{penalty} = n \cdot \frac{b\_{liq}}{10,000} = q \cdot P \cdot \frac{b\_{liq}}{10,000} \tag{S.4}
$$

### Batch Accounting

Across a batch of fills, the Exchange's revenue is exactly what came in as taker fees (S.2) minus what went out as maker rebates (S.3): $$F$$ is the sum of per-fill taker fees and $$R$$ the sum of per-fill rebates. Liquidation penalties (S.4) are deliberately excluded — they belong to the insurance fund, not the Exchange. Because $$F$$ and $$R$$ are sums of the already-truncated per-fill amounts, the identity holds exactly with no further rounding.

$$
V = F - R \tag{S.5}
$$

### Funding Settlement

Funding settlement converts each account's signed funding payment into a transfer against the funding pool. The transferred amount is always the magnitude $$|x|$$; the sign selects the direction. A positive $$x$$ means the account pays: the transfer runs account → funding pool. A negative $$x$$ means the account receives: the transfer runs funding pool → account, carrying $$|x|$$. A zero payment produces no transfer at all, and funding settlement contributes nothing to fee totals or revenue.

$$
\text{transfer} = |x|, \qquad \text{direction} = \begin{cases} \text{account} \to \text{pool} & x > 0 \ \text{pool} \to \text{account} & x < 0 \ \text{none} & x = 0 \end{cases} \tag{S.6}
$$

### Batch Accounting

Settlement records compose additively. Merging a set of records concatenates their transfer lists and sums their fee totals and revenue, while the merged period spans from the earliest period start to the latest period end. Because every total is a plain sum, the merged record's revenue is the sum of the constituent revenues — merging never changes any amount, only groups them.

$$
X\_{\text{merged}} = \sum\_j X\_j \quad \text{for } X \in {V, F, R, \text{net}} \tag{S.7}
$$

## Invariants

* Fee accounting closes exactly: for any settlement record, $$V = F - R$$, where $$F$$ is the sum of all taker-fee transfer amounts and $$R$$ the sum of all maker-rebate transfer amounts ((S.5)). *Why it holds:* Each fill appends exactly one taker-fee and one maker-rebate transfer and adds the same two truncated amounts to the running totals $$F$$ and $$R$$; revenue is computed once at the end as $$F - R$$. No other path mutates these totals, and merging sums them component-wise, so closure is preserved under batching.
* Taker fees, maker rebates, and liquidation penalties are all non-negative: $$\text{fee}\_t \ge 0$$, $$\text{rebate}\_m \ge 0$$, $$\text{penalty} \ge 0$$. *Why it holds:* Each amount is a product of a positive notional $$q \cdot P$$ and a non-negative rate — the maker rate enters through its absolute value $$|b\_m|$$ — and truncation toward zero cannot cross zero. Hence no transfer amount is ever negative.
* Liquidation penalties route only to the insurance fund, never to the Exchange fee account, so $$V$$ contains no penalty revenue ((S.4)). *Why it holds:* The penalty transfer is constructed with the insurance fund as its fixed destination, and the penalty amount is never added to $$F$$ or $$R$$. The revenue identity therefore sees only fees and rebates.
* Funding settlement is conservative through the pool: every nonzero payment produces exactly one transfer of magnitude $$|x|$$, directed by the sign of $$x$$, and funding records carry zero fees, rebates, and revenue ((S.6)). *Why it holds:* The settle-funding path branches only on the sign of the payment, emitting one transfer with amount $$x$$ or $$|x|$$ and skipping zero amounts; its record hard-codes all fee totals to zero. So funding moves money between accounts and the pool without creating revenue.

## Worked example

Consider a single fill of $$q = 1$$ BTC at $$P = 50{,}000$$ USDX in a market with $$b\_t = 5$$, $$b\_m = -2$$, and $$b\_{liq} = 5$$ basis points. The notional (S.1) is $$n = 1 \times 50{,}000 = 50{,}000$$ USDX. The taker fee (S.2) is $$50{,}000 \times 5/10{,}000 = 25$$ USDX, paid from the taker to the Exchange fee account. The maker rebate (S.3) uses the magnitude of the stored rate: $$50{,}000 \times 2/10{,}000 = 10$$ USDX, paid from the Exchange fee account to the maker.

If this were an ordinary fill, settlement would stop there and record net revenue (S.5) of $$V = 25 - 10 = 15$$ USDX. If instead the fill is a liquidation, one more transfer is added: a penalty (S.4) of $$50{,}000 \times 5/10{,}000 = 25$$ USDX from the taker to the insurance fund. Note that $$V$$ is still $$15$$ USDX — the penalty capitalizes the insurance fund and never counts as Exchange revenue.

Funding settles separately. Suppose the same account owes a funding payment of $$x = 100$$ USDX: since $$x > 0$$, settlement emits a transfer of $$100$$ USDX from the account to the funding pool (S.6). Had the payment been $$x = -75$$ USDX, the pool would instead pay the account $$75$$ USDX. Neither transfer touches $$F$$, $$R$$, or $$V$$.

## Analysis

### Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

| Expression             | Input                     | Elasticity ε |
| ---------------------- | ------------------------- | ------------ |
| `fill_notional`        | size                      | 1            |
| `fill_notional`        | price                     | 1            |
| `taker_fee`            | size                      | 1            |
| `taker_fee`            | price                     | 1            |
| `taker_fee`            | taker\_fee\_bps           | 1            |
| `maker_rebate`         | size                      | 1            |
| `maker_rebate`         | price                     | 1            |
| `maker_rebate`         | maker\_rebate\_bps        | 1            |
| `liquidation_penalty`  | size                      | 1            |
| `liquidation_penalty`  | price                     | 1            |
| `liquidation_penalty`  | liquidation\_penalty\_bps | 1            |
| `net_exchange_revenue` | total\_fees               | 1.667        |
| `net_exchange_revenue` | total\_rebates            | -0.6667      |
| `funding_transfer`     | funding\_amount           | 1            |
| `merge_totals`         | volume\_1                 | 0.625        |
| `merge_totals`         | volume\_2                 | 0.375        |

![Sensitivity tornado — Taker fee](/files/uKkLBRpqMbhpbdV32wLV)

![Sensitivity tornado — Net exchange revenue](/files/Ykd9SkqGdTsksiuJnV4M)

### Response curves

![The taker fee grows linearly in price at each fill size, holding the taker rate fixed at 5 basis points.](/files/FbioQFdSF9zdQSZmvVwx)

*The taker fee grows linearly in price at each fill size, holding the taker rate fixed at 5 basis points.*

![Net revenue is fees minus rebates: each rebate level shifts the same unit-slope line downward, holding rebates constant along each series.](/files/mmQNkVBlFimrjzjAWhA6)

*Net revenue is fees minus rebates: each rebate level shifts the same unit-slope line downward, holding rebates constant along each series.*

![The transferred amount is the magnitude of the signed funding payment; the sign only selects the direction of the transfer, with zero producing no transfer.](/files/0S2jstRJhpo4c1qcLM10)

*The transferred amount is the magnitude of the signed funding payment; the sign only selects the direction of the transfer, with zero producing no transfer.*

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.
* Sibling model: [funding-rate](/math-engine/funding-rate)
* Sibling model: [insurance-fund](/math-engine/insurance-fund)
* Sibling model: [liquidation-engine](/math-engine/liquidation-engine)
* Sibling model: [margin-math](/math-engine/margin-math)
* Sibling model: [oracle](/math-engine/oracle)
* Sibling model: [order-book](/math-engine/order-book)
* Sibling model: [position-tracker](/math-engine/position-tracker)


# Performance

The matching engine is a message-passing machine: each market is an actor — a dedicated OS thread that exclusively owns its order book, position map, and trigger state — and the `MatchingEngine` is a router that maps a market id to that actor's bounded mpsc sender. Every operation the exchange performs is therefore decomposable into a routing step (which actors receive the message, and how the caller learns which one owns the answer), a queueing step (mailbox depth and reply via `oneshot`), and an on-actor computation step (book traversal, position mutation, cache refresh). Because these steps are structural, each benchmarked operation admits a falsifiable scaling law derived from the topology itself: direct dispatch is flat in market count, broadcast fan-out is linear in it, bulk cancellation is linear in resting orders, and concurrent submission into a single actor exhibits the serialization-plus-coherence shape of the Universal Scalability Law.

The models below carry only symbolic constants ($$c\_0, c\_1, \dots$$): the *form* of each law comes from reading the actor architecture and the benchmark's timed closure, while the constants are machine properties of the CI runner and are fitted separately against the measured benchmark series. Each operation corresponds exactly to one measured sweep, so the fit is a direct confrontation of theory with instrument.

The residuals are the signal. If direct routing drifts upward with market count, the router's market map is not $$O(1)$$; if the contention series needs a large quadratic coefficient, point-to-point serialization has acquired a coherence cost; if bulk cancel bends superlinearly, the book's removal path is not the linear scan the model claims. The model is the hypothesis; the CI bench is the experiment.

Provenance: the scaling laws are **derived from source** (the actor architecture and data structures of the hot paths); the constants are **fitted to CI measurement** (a benchmark run captured 2026-07-01); the residuals are the comparison signal — model error, or a caught regression.

## The architecture, mathematically

Formally, the engine's matching-engine service is a set of $$M$$ single-writer actors $${A\_1, \dots, A\_M}$$, one per market, each spawned with `std::thread::spawn` and owning its state exclusively. Tokio handler tasks never touch market state; they send a message carrying a `oneshot` reply channel into the actor's bounded mpsc mailbox and suspend until the reply arrives. The cost of any request is thus $$T = t\_{route} + t\_{queue} + t\_{work} + t\_{reply}$$, where $$t\_{route}$$ depends on how many senders the router must touch and $$t\_{work}$$ runs serially on the owning actor's thread.

Parallelism and contention follow directly. Requests targeting *different* markets never share a lock — throughput scales with cores up to $$M$$ — but requests targeting the *same* market are serialized by that market's single mailbox: the actor is an $$M/M/1$$-like server, and adding submitters to one market adds queueing, not parallelism. This is a deliberate trade: the previous mutex-sharded design allowed races between the book lock and the position lock and exhausted tokio workers with synchronous matching work; the actor design buys serialized market access and predictable latency at the price of a per-market serial ceiling.

Routing topology is the other axis. A call that names its market (`MatchingEngine::cancel_order_in_market`) resolves one sender — flat in $$M$$. A call that does not (`MatchingEngine::cancel_order` with only an order id) must fan out to every actor, paying $$M-1$$ lookup misses — linear in $$M$$. Cross-margin risk lives outside the actors in `risk_margin::RiskModule`, whose equity cache aggregates over an account's $$n$$ positions per refresh and whose liquidation path (`liquidate_portfolio`) closes each of $$n$$ positions through its owning market actor in turn. The six operations below each isolate one of these structural costs.

## Single-order cancel — direct dispatch

Each sample cancels a fixed batch of 400 pre-rested orders on MARKET-00 via `MatchingEngine::cancel_order_in_market(id, account, market_id)` while the engine hosts $$n$$ market actors (`bench_cancel_routing` in `tests/benchmarks/benches/cancel_order.rs`, mode="direct"). Because the caller names the owning market, the router resolves exactly one sender from its market map and dispatches to that single actor; the other $$n-1$$ actors are never messaged. The per-batch cost is therefore one map lookup, one mailbox round-trip, and one book removal per cancel — independent of how many markets exist. The predicted law is a constant.

$$
T(n) = c\_0
$$

*Architecture: Measures the flatness of keyed routing: a named-market dispatch touches one mailbox regardless of topology size, so any measured slope in n falsifies the O(1) market-map claim.*

Fitted to the CI series: $$c0 = \text{6.21ms}$$; $$R^2$$ n/a for a constant model (O(1)).

![Single-order cancel — direct dispatch model vs measurement](/files/23MllnLLSeRll2siMMfh)

*Dots: CI measurements. Curve: the source-derived model with fitted constants. Labels: residuals.*

## Single-order cancel — broadcast fan-out

Identical setup to the direct series, but each cancel goes through `MatchingEngine::cancel_order(id, account)` with no market id (`bench_cancel_routing`, mode="broadcast"). The router cannot know which actor owns the order, so it fans the cancel out to all $$n$$ market actors; $$n-1$$ of them perform a lookup miss and reply not-found, and one performs the removal. Each cancel thus pays $$n$$ mailbox round-trips instead of one, so the per-batch cost grows linearly in market count on top of the fixed removal work. The structural contrast with the direct series is the point: same book operation, different routing topology.

$$
T(n) = c\_0 + c\_1 , n
$$

*Architecture: Measures the linear price of ownership-blind routing: without a market hint, the actor topology forces an n-way fan-out with n−1 wasted mailbox round-trips per cancel.*

Fitted to the CI series: $$c0 = \text{1.19ms}$$, $$c1 = \text{1.17ms}$$; $$R^2 = 1.0000$$ (O(n)).

![Single-order cancel — broadcast fan-out model vs measurement](/files/7q6EcYpLL1tEo8P5KhGQ)

*Dots: CI measurements. Curve: the source-derived model with fitted constants. Labels: residuals.*

## Bulk cancel of all resting orders on a market

One book is seeded with $$n$$ non-crossing resting buys at distinct 0.5-tick prices, then a single `cancel_all_orders_for_market(account, market)` call is timed (`bench_cancel_all_for_market` in `cancel_order.rs`). The call routes once to the owning actor, which walks the account's resting orders and removes each from the book — one price-level removal and one order-state transition per order. The dominant cost is the per-order removal loop, so the law is affine in $$n$$; the intercept captures the single routing round-trip and call overhead. `Throughput::Elements(n)` in the bench exposes the same law as a per-order rate.

$$
T(n) = c\_0 + c\_1 , n
$$

*Architecture: Measures the on-actor serial work of a bulk book mutation: one routing hop amortized over n removals, isolating the per-order removal cost of the book data structure.*

Fitted to the CI series: $$c0 = \text{-654µs}$$, $$c1 = \text{1.21µs}$$; $$R^2 = 0.9996$$ (O(n)).

![Bulk cancel of all resting orders on a market model vs measurement](/files/i6sdxR9mPujzfhQdohOc)

*Dots: CI measurements. Curve: the source-derived model with fitted constants. Labels: residuals.*

## Concurrent submitters into one market — contention scaling

$$n$$ tokio tasks each submit 1000 crossing limit orders (distinct account pairs per task) into a single-market engine on a current-thread runtime, and the wall-clock for all tasks to drain is timed (`bench_contention` in `tests/benchmarks/benches/engine_throughput.rs`). Total work is $$1000n$$ orders, but the market is one actor: every order serializes through the same mailbox, so the linear term $$c\_1 n$$ is the actor's per-1000-order matching cost — the serialization term of the Universal Scalability Law. The quadratic term $$c\_2 n^2$$ captures the superlinear costs of adding submitters to a saturated single-writer pipeline: mailbox contention on the shared mpsc sender, task-switching and wakeup churn among $$n$$ suspended submitters per reply, and cache-line ping-pong on the shared channel state — the USL coherence term. $$c\_0$$ absorbs spawn/join overhead.

$$
T(n) = c\_0 + c\_1 , n + c\_2 , n^2
$$

*Architecture: Measures the single-writer ceiling of the per-market actor: work scales linearly with submitters (serialization) while cross-task coherence on the shared mailbox adds a quadratic penalty — the USL shape of the architecture's one deliberate serialization point.*

Fitted to the CI series: $$c0 = \text{17ms}$$, $$c1 = \text{1.68ms}$$, $$c2 = \text{77.6µs}$$; $$R^2 = 0.9994$$ (O(n^2)).

![Concurrent submitters into one market — contention scaling model vs measurement](/files/fhAy2xapNFpuRzKD0jqk)

*Dots: CI measurements. Curve: the source-derived model with fitted constants. Labels: residuals.*

## Equity cache refresh for a cross-margin account

A `RiskModule` is snapshot-built with one account holding $$n$$ cross-mode positions across $$n$$ markets, and the timed loop calls `risk.refresh_equity_for(ACCOUNT, "fill")` repeatedly on the hot cache (`bench_b7_equity_cache_update` in `tests/benchmarks/benches/cross_margin.rs`; setup via `build_account_snapshot`). A refresh recomputes account equity as collateral plus the sum of unrealized PnL over every open position, each mark-to-market against its market's oracle price — one position-map traversal and one oracle lookup per position. The work is a fold over $$n$$ positions with constant per-element cost, so the law is affine; the intercept is the fixed cost of the call, account lookup, and cache write.

$$
T(n) = c\_0 + c\_1 , n
$$

*Architecture: Measures the per-position cost of cross-margin aggregation in the risk module — the price every fill pays to keep account equity coherent grows linearly with portfolio breadth.*

Fitted to the CI series: $$c0 = \text{-12.3ns}$$, $$c1 = \text{278ns}$$; $$R^2 = 0.9997$$ (O(n)).

![Equity cache refresh for a cross-margin account model vs measurement](/files/qHJFsj2VihipSjk30A5h)

*Dots: CI measurements. Curve: the source-derived model with fitted constants. Labels: residuals.*

## Cross-margin portfolio liquidation roundtrip

Each iteration builds a fresh account holding $$n$$ cross-mode positions across $$n$$ markets (liquidation mutates state, so setup is per-iteration and outside the timed window) and times a single `risk.liquidate_portfolio(ACCOUNT).await` (`bench_b9_liquidation_roundtrip` in `cross_margin.rs`). The liquidation walks the account's portfolio and, for each of the $$n$$ positions, executes a closure through the owning market actor — a routing hop, a mailbox round-trip, and a position-close on that actor — plus the per-position risk accounting (equity/margin updates, insurance-fund interaction). Since each position lives on a distinct market actor and closures are issued sequentially by the risk module, the cost is one fixed portfolio-scan overhead plus $$n$$ per-market roundtrips: affine in $$n$$.

$$
T(n) = c\_0 + c\_1 , n
$$

*Architecture: Measures the cross-actor roundtrip cost of risk-driven control flow: liquidation crosses the risk-module/actor boundary once per position, so c1 is the price of one full risk→actor→risk closure hop.*

Fitted to the CI series: $$c0 = \text{3.92µs}$$, $$c1 = \text{1.07µs}$$; $$R^2 = 0.9990$$ (O(n)).

![Cross-margin portfolio liquidation roundtrip model vs measurement](/files/0MR5ltHePD3qJ0pxdPzF)

*Dots: CI measurements. Curve: the source-derived model with fitted constants. Labels: residuals.*

## Notes

* All constants are machine properties of the CI runner (shared GitHub-hosted hardware): absolute values are not portable across machines, and run-to-run variance on shared runners inflates residuals — the scaling shape, not the constant magnitudes, is the falsifiable claim.
* The concurrent\_submitters quadratic term is a USL-shaped phenomenological approximation of coherence costs (mailbox contention, scheduler churn), not a first-principles derivation; with only four swept points (n=1,2,4,8) the c2 fit is weakly identified.
* Batched benchmarks (routing series: 400 cancels/sample; throughput series: 1000 orders/task) report per-batch wall-clock, so fitted constants are per-batch, not per-operation; divide by the batch size for per-op costs.
* cancel\_all\_for\_market may hide a log(n) factor from ordered price-level removal inside c1 over the swept range (1000–16000); a systematic upward bend in residuals at large n would indicate it.
* The direct-routing model T(n)=c0 is deliberately slope-free: any statistically significant slope in the direct series is a falsification signal (router map not O(1) or idle-actor overhead), not something the model should absorb.
* Series sweep points are sparse (3–4 values each), so fits distinguish model families (constant vs linear vs quadratic) rather than resolving fine functional structure such as log factors.
* Timing on the CI runner uses coarse sample counts (sample\_size=10 for destructive benches), so per-point uncertainty should be propagated into the fit rather than assuming homoscedastic noise.

## References

* Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.


# Building on Nexus

Nexus is fully EVM-compatible, so you can build with the tools you already know — Solidity, Hardhat, Foundry, Remix, and the standard libraries — and deploy to a chain with a high-performance financial engine built in.

Start here:

* [Getting Started](/network/building-on-nexus/getting-started) — deploy your first contract.
* [Developer Environment Setup](/network/building-on-nexus/developer-environment-setup) — networks, RPCs, and toolchain config.
* [Deploying on NexusEVM](/network/building-on-nexus/deploying-on-nexusevm) — deployment details and NexusCore composability.
* [Endpoints](/network/building-on-nexus/endpoints) — RPC and API endpoints, chain IDs, and limits.
* [Tokens and Bridges](/network/building-on-nexus/tokens-and-bridges) — NEX, WNEX, and bridging.

NEX is the native gas token. See [NEX](/network/building-on-nexus/tokens-and-bridges/usdnex) and the [Architecture](/architecture/architecture) section for how NexusEVM and NexusCore fit together.


# Getting Started

#### Prerequisites

* **Wallet** — MetaMask or any EVM-compatible wallet, configured for Nexus (See [Developer Environment Setup](https://docs.nexus.xyz/network/building-on-nexus/developer-environment-setup))
* **Testnet tokens** — NEX on testnet for gas ([faucet](https://faucet.nexus.xyz))
* **Toolchain** — Hardhat or Foundry

#### Quickstart: Deploy a contract on NexusEVM

**Step 1 — Create a Foundry project**

```bash
mkdir nexus-token && cd nexus-token
forge init
```

**Step 2 — Write the contract**

Replace `src/Counter.sol` with `src/NexusToken.sol`:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract NexusToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("NexusToken", "NTK") {
        _mint(msg.sender, initialSupply);
    }
}
```

Install OpenZeppelin:

```bash
forge install OpenZeppelin/openzeppelin-contracts --no-commit
```

**Step 3 — Deploy to Nexus Testnet**

```bash
forge create src/NexusToken.sol:NexusToken \
  --rpc-url https://testnet.rpc.nexus.xyz \
  --chain-id 3945 \
  --private-key $PRIVATE_KEY \
  --constructor-args 1000000000000000000000000
```

This deploys 1,000,000 NTK to your address.

**Step 4 — Read on-chain state**

Check your balance:

```bash
cast call <CONTRACT_ADDRESS> "balanceOf(address)" $YOUR_ADDRESS \
  --rpc-url https://testnet.rpc.nexus.xyz
```

Transfer tokens:

```bash
cast send <CONTRACT_ADDRESS> "transfer(address,uint256)" \
  <RECIPIENT_ADDRESS> 1000000000000000000 \
  --rpc-url https://testnet.rpc.nexus.xyz \
  --private-key $PRIVATE_KEY
```

**Step 5 — Deploy to mainnet**

When ready, switch to mainnet:

```bash
forge create src/NexusToken.sol:NexusToken \
  --rpc-url https://mainnet.rpc.nexus.xyz \
  --chain-id 3946 \
  --private-key $PRIVATE_KEY \
  --constructor-args 1000000000000000000000000
```

Finality is single-slot — the deployment is confirmed as soon as the block is committed.

#### Quickstart: Interact with the chain using cast

No project setup needed. These commands work immediately with Foundry installed.

Get the latest block number:

```bash
cast block-number --rpc-url https://mainnet.rpc.nexus.xyz
```

Read any contract's state:

```bash
cast call <CONTRACT_ADDRESS> "symbol()" --rpc-url https://mainnet.rpc.nexus.xyz
```

Check an address balance:

```bash
cast balance <ADDRESS> --rpc-url https://mainnet.rpc.nexus.xyz
```

Send a transaction:

```bash
cast send <TO_ADDRESS> --value 0.1ether \
  --rpc-url https://mainnet.rpc.nexus.xyz \
  --private-key $PRIVATE_KEY
```

All transactions confirm with single-slot finality. No need to wait for additional block confirmations.


# Developer Environment Setup

### RPC Endpoints

<table><thead><tr><th>Network</th><th width="86.41796875">Chain ID</th><th width="276">HTTP RPC</th><th width="150.3046875">WebSocket RPC</th><th width="94.90234375">Status</th></tr></thead><tbody><tr><td>Nexus Mainnet</td><td>3946</td><td><code>https://mainnet.rpc.nexus.xyz</code></td><td><code>wss://mainnet.rpc.nexus.xyz</code></td><td>Live</td></tr><tr><td>Nexus Testnet</td><td>3945</td><td><code>https://testnet.rpc.nexus.xyz</code></td><td><code>wss://testnet.rpc.nexus.xyz</code></td><td>Live</td></tr></tbody></table>

Notes:

* WebSocket connections require an `X-Api-Key` header on the upgrade handshake. Since browsers cannot set custom headers on WebSocket connections, WSS is currently limited to server-side use (Node.js, Python, etc). Browser dApps should use HTTP polling or a server-side relay.
* OFAC-restricted jurisdictions are blocked at the edge. See [Restricted Jurisdictions](https://nexus.xyz/restricted-jurisdictions).

### Request Limits

| Limit                  | Value  | Error                                 |
| ---------------------- | ------ | ------------------------------------- |
| Max payload size       | 2 MB   | JSON-RPC `-32005`                     |
| Max batch sub-requests | 10     | JSON-RPC `-32005`                     |
| Per-IP rate limiting   | Active | JSON-RPC error + `Retry-After` header |

### Testnet Faucet

Developers can acquire Testnet NEX here: <https://faucet.nexus.xyz>

### Wallet Setup

| Field              | Mainnet                         | Testnet                              |
| ------------------ | ------------------------------- | ------------------------------------ |
| Network Name       | Nexus Mainnet                   | Nexus Testnet                        |
| RPC URL            | `https://mainnet.rpc.nexus.xyz` | `https://testnet.rpc.nexus.xyz`      |
| Chain ID           | `3946`                          | `3945`                               |
| Currency Symbol    | `NEX`                           | `NEX`                                |
| Block Explorer URL | `https://explorer.nexus.xyz`    | `https://testnet.explorer.nexus.xyz` |

To add programmatically:

```javascript
await window.ethereum.request({
  method: "wallet_addEthereumChain",
  params: [{
    chainId: "0xF6A",  // 3946 in hex
    chainName: "Nexus Mainnet",
    nativeCurrency: {
      name: "NEX",
      symbol: "NEX",
      decimals: 18,
    },
    rpcUrls: ["https://mainnet.rpc.nexus.xyz"],
    blockExplorerUrls: ["https://explorer.nexus.xyz"],
  }],
});
```

### Hardhat

```bash
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
```

`hardhat.config.ts`:

```typescript
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
  solidity: "0.8.24",
  networks: {
    nexusMainnet: {
      url: process.env.NEXUS_RPC_URL || "https://mainnet.rpc.nexus.xyz",
      chainId: 3946,
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    },
    nexusTestnet: {
      url: process.env.NEXUS_TESTNET_RPC_URL || "https://testnet.rpc.nexus.xyz",
      chainId: 3945,
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    },
  },
};

export default config;
```

### Foundry

```bash
curl -L https://foundry.paradigm.xyz | bash
foundryup
```

`foundry.toml`:

```toml
[profile.default]
solc_version = "0.8.24"

[rpc_endpoints]
nexus_mainnet = "https://mainnet.rpc.nexus.xyz"
nexus_testnet = "https://testnet.rpc.nexus.xyz"
```

```bash
forge create src/MyContract.sol:MyContract \
  --rpc-url nexus_testnet \
  --chain-id 3945 \
  --private-key $PRIVATE_KEY
```

### Remix

1. Open [Remix IDE](https://remix.ethereum.org/)
2. Deploy & Run tab → "Injected Provider — MetaMask"
3. Ensure MetaMask is connected to Nexus (chain ID 3946 or 3945)

### Environment verification

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

Expected: a valid hex block number in the `result` field.

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
```

Expected: `"0xf6a"` (3946) for mainnet, `"0xf69"` (3945) for testnet.


# Deploying on NexusEVM

NexusEVM is fully EVM-compatible.

#### Deployment with Hardhat

```bash
npx hardhat run scripts/deploy.ts --network nexusMainnet
```

Verify deployment:

```bash
cast code 0xYOUR_CONTRACT_ADDRESS --rpc-url https://mainnet.rpc.nexus.xyz
```

#### Deployment with Foundry

See [Developer Environment Setup](https://docs.nexus.xyz/network/building-on-nexus/developer-environment-setup) for configuration.

```bash
forge create src/MyContract.sol:MyContract \
  --rpc-url nexus_mainnet \
  --chain-id 3946 \
  --private-key $PRIVATE_KEY
```

With constructor arguments:

```bash
forge create src/MyContract.sol:MyContract \
  --rpc-url nexus_mainnet \
  --chain-id 3946 \
  --private-key $PRIVATE_KEY \
  --constructor-args "arg1" 42
```

#### Gas

Gas metering follows Ethereum's model. All opcodes cost the same gas as on Ethereum. Gas limits are enforced — out-of-gas transactions revert normally. **Gas is paid in NEX.**

#### Differences from Ethereum

| Difference           | Detail                                                                                                                                      |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Block time           | 1 second (vs. Ethereum's 12s). `block.timestamp` increments faster — time-based logic works, but blocks-as-time proxies behave differently. |
| Finality             | Single-slot. No reorgs, no uncle blocks. One confirmation is sufficient. `block.number` is always authoritative.                            |
| Transaction ordering | CometBFT default mempool (FIFO). No gas auction, no MEV competition.                                                                        |
| Native currency      | NEX. `msg.value` transfers NEX.                                                                                                             |
| ENS                  | Not available on NexusEVM.                                                                                                                  |

#### Composability with the Exchange

When the Exchange launches, NexusEVM contracts will be able to call into NexusCore — placing orders, reading market data, and managing margin atomically within a single EVM transaction.


# Tokens and Bridges

## NEX

NEX is the native token of the Nexus blockchain; gas is paid in NEX. WNEX is the wrapped ERC-20 version of NEX on the Nexus blockchain, and on Ethereum NEX also exists as an ERC-20. See [NEX](/network/building-on-nexus/tokens-and-bridges/usdnex) for details.

## USDX *(In Development)*

USDX will be the margin and quote currency of the Nexus Exchange — traders will use it to open positions and settle trades. It is in development in collaboration with M0 Protocol.

## Bridging

NEX moves across chains through two mechanisms:

* The **canonical NEX bridge** connects the Nexus blockchain and Ethereum.
* A **Hyperlane Warp Route** connects Ethereum and Binance Smart Chain (BSC): NEX is locked on Ethereum and a synthetic NEX is minted on BSC.

See [Bridging](/network/building-on-nexus/tokens-and-bridges/bridging) for contracts and step-by-step instructions.


# NEX

NEX is the native token of the Nexus blockchain. On Ethereum, it exists as an ERC-20. On BSC, it exists as a Hyperlane synthetic ERC-20.

#### What NEX does

| Role                | Detail                                                           |
| ------------------- | ---------------------------------------------------------------- |
| Gas                 | Every transaction on Nexus pays gas in NEX                       |
| Smart contracts     | Executing smart contracts deployed on the Nexus blockchain       |
| Network interaction | Interacting with the network's consensus and compute networks    |
| Fee discounts       | Holding NEX provides trading-fee discounts on the Nexus Exchange |

#### Supply

| Metric       | Value                      |
| ------------ | -------------------------- |
| Total supply | 100,000,000,000,000 (100T) |

See [Tokenomics](/overview/tokenomics) for total supply, token utility, distribution status, and the vesting schedule.

#### Contract addresses

| Network       | Address                                      | Type                       |
| ------------- | -------------------------------------------- | -------------------------- |
| Nexus Mainnet | NA                                           | Native gas token           |
| Ethereum      | `0xf57D49646621F563b0B905aFc8336923AC569Ec5` | ERC-20                     |
| BSC           | `0x365DE036A1F7dcCb621530d517133521debB2013` | Hyperlane synthetic ERC-20 |

The ERC-20 contract includes mint and burn functions used for bridge operations via the canonical NEX bridge.

#### Wrapped NEX

WNEX is a standard WETH9/ERC-20 wrapper — `deposit`, `withdraw`, `transfer`, `approve` all work as expected.

WNEX has 18 decimals.

| Contract             | Network              | Address                                      |
| -------------------- | -------------------- | -------------------------------------------- |
| WNEX (Wrapped Nexus) | Nexus Mainnet (3946) | `0x69BFfB3C9cb3aa81E8ab23dad470246956D2cC7F` |

#### Informational Links

<table><thead><tr><th width="284.21484375">Platform</th><th width="435.6953125">Link</th></tr></thead><tbody><tr><td>CoinMarketCap</td><td><a href="https://coinmarketcap.com/currencies/nexus-labs/">https://coinmarketcap.com/currencies/nexus-labs/</a></td></tr><tr><td>CoinGecko</td><td><a href="https://www.coingecko.com/en/coins/nexus-4">https://www.coingecko.com/en/coins/nexus-4</a></td></tr><tr><td>Etherscan</td><td><a href="https://etherscan.io/token/0xf57d49646621f563b0b905afc8336923ac569ec5">https://etherscan.io/token/0xf57d49646621f563b0b905afc8336923ac569ec5</a></td></tr></tbody></table>

#### Adding to your wallet

**Nexus (MetaMask or EVM wallet)**

Add the Nexus network to MetaMask. NEX appears automatically as the native gas token — no token import needed.

**Ethereum (ERC-20)**

Import the token in MetaMask:

1. Switch to Ethereum Mainnet
2. Click **Import tokens**
3. Enter contract address: `0xf57D49646621F563b0B905aFc8336923AC569Ec5`
4. Symbol and decimals auto-populate: `NEX`, `18`

**BSC (Hyperlane synthetic)**

Import the token in MetaMask:

1. Switch to BNB Smart Chain
2. Click Import tokens
3. Enter contract address: `0x365DE036A1F7dcCb621530d517133521debB2013`
4. Symbol and decimals auto-populate: NEX, 18


# Bridging

NEX moves between Ethereum and BSC via a Hyperlane Warp Route. On Ethereum, NEX is locked in a collateral contract. On BSC, a synthetic NEX is minted 1:1 against the locked balance.

You can bridge NEX between Ethereum and BSC: [Nexus Hyperlane Warp Route](https://nexus.hyperlane.xyz/?origin=ethereum\&originToken=NEX\&destination=bsc\&destinationToken=NEX).

**Ethereum → BSC**

1. Deposit ERC-20 NEX into the Hyperlane collateral contract on Ethereum.
2. Hyperlane validators attest to the deposit (6-of-9 multisig threshold).
3. Synthetic NEX is minted on BSC and delivered to your wallet.

**BSC → Ethereum**

1. Synthetic NEX is burned on BSC.
2. Hyperlane validators attest to the burn (4-of-6 multisig threshold).
3. ERC-20 NEX is released from the collateral contract on Ethereum and delivered to your wallet.

**Contract addresses**

| Contract          | Ethereum                                                  | BSC                                                      |
| ----------------- | --------------------------------------------------------- | -------------------------------------------------------- |
| Warp Route token  | `0xb71183f48F7f7789D40551149bE28a51f9A97F9A` (collateral) | `0x365DE036A1F7dcCb621530d517133521debB2013` (synthetic) |
| NEX ERC-20        | `0xf57D49646621F563b0B905aFc8336923AC569Ec5`              | —                                                        |
| Hyperlane Mailbox | `0xc005dc82818d67AF737725bD4bf75435d065D239`              | `0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4`             |

The collateral contract is a TransparentUpgradeableProxy pointing to the Hyperlane canonical HypERC20Collateral implementation. The BSC synthetic is a TransparentUpgradeableProxy pointing to the Hyperlane canonical HypERC20 implementation. Both are verified on Etherscan and BscScan.

**Fees and timing**

| Parameter              | Value                                                                                  |
| ---------------------- | -------------------------------------------------------------------------------------- |
| Bridge fee (ETH → BSC) | 6 bps + gas fees on both source and destination chains                                 |
| Bridge fee (BSC → ETH) | Gas fees on both source and destination chains                                         |
| Transfer time          | Typically under 5 minutes. Depends on validator attestation and source-chain finality. |

**Security**

The bridge uses Hyperlane's Interchain Security Module (ISM) stack:

* ETH → BSC: 6-of-9 validator multisig with dual MerkleRoot and MessageId verification.
* BSC → ETH: 4-of-6 validator multisig with dual MerkleRoot and MessageId verification.

Both warp route contracts are owned by the Nexus Foundation multisig. Proxy upgrades are controlled by the Foundation multisig via OpenZeppelin ProxyAdmin contracts.

**Troubleshooting**

Transfer pending — Transfers require attestation from Hyperlane validators after source-chain finality. If pending for more than 10 minutes, check the [Hyperlane Explorer](https://explorer.hyperlane.xyz/).

Insufficient gas — You need gas tokens on the source chain: ETH for Ethereum → BSC, BNB for BSC → Ethereum.

Transaction reverted — Check that you have approved sufficient token allowance for the Warp Route collateral contract (Ethereum side) and have enough gas on the source chain.

For Hyperlane protocol details, see [Hyperlane documentation](https://docs.hyperlane.xyz/).


# Endpoints

For RPC URLs, chain IDs, wallet setup, and developer tooling configuration, see [Developer Environment Setup](https://docs.nexus.xyz/network/building-on-nexus/developer-environment-setup).

## RPC

### Request Format

All requests use the standard JSON-RPC 2.0 envelope:

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2c620e"
}
```

#### Batch Requests

Send an array of request objects to execute multiple calls in a single HTTP round-trip. Maximum **10 sub-requests** per batch.

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '[
    {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1},
    {"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":2},
    {"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":3}
  ]'
```

### Request limits

| Limit                  | Value        | Error                                 |
| ---------------------- | ------------ | ------------------------------------- |
| Max payload size       | 2 MB         | JSON-RPC `-32005`                     |
| Max batch sub-requests | 10 per batch | JSON-RPC `-32005`                     |
| Per-IP rate limiting   | Active       | JSON-RPC error + `Retry-After` header |

All rejections return standard JSON-RPC error responses. When rate-limited, back off and retry after the duration specified in the `Retry-After` header.

### Supported Methods

#### `eth` Namespace

Standard Ethereum execution API methods. All methods accept `latest`, `earliest`, `pending`, or a hex block number as the block parameter unless noted otherwise.

**Reading Chain State**

| Method                     | Description                                                                             |
| -------------------------- | --------------------------------------------------------------------------------------- |
| `eth_blockNumber`          | Returns the current block height.                                                       |
| `eth_chainId`              | Returns the chain ID (`0xf6a` for mainnet, `0xf69` for testnet).                        |
| `eth_gasPrice`             | Returns the current gas price in wei.                                                   |
| `eth_maxPriorityFeePerGas` | Returns the suggested priority fee (tip) in wei.                                        |
| `eth_feeHistory`           | Returns base fee and gas usage history for a range of blocks.                           |
| `eth_blobBaseFee`          | Returns the blob base fee. Always returns `0x1` on Nexus (EIP-4844 blobs are not used). |
| `eth_syncing`              | Returns `false` when synced, or sync progress object when syncing.                      |

**Example: `eth_gasPrice`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6b"
}
```

**Accounts and Balances**

| Method                    | Parameters                                | Description                                                       |
| ------------------------- | ----------------------------------------- | ----------------------------------------------------------------- |
| `eth_getBalance`          | `address`, `blockNumber`                  | Returns the NEX balance of an address in wei.                     |
| `eth_getTransactionCount` | `address`, `blockNumber`                  | Returns the nonce (number of transactions sent from the address). |
| `eth_getCode`             | `address`, `blockNumber`                  | Returns the contract bytecode at the address, or `0x` for EOAs.   |
| `eth_getStorageAt`        | `address`, `position`, `blockNumber`      | Returns the value in a contract's storage slot.                   |
| `eth_getProof`            | `address`, `storageKeys[]`, `blockNumber` | Returns the Merkle proof for an account and its storage slots.    |
| `eth_accounts`            | —                                         | Returns an empty array (no managed accounts on public RPC).       |

**Example: `eth_getBalance`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": ["0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", "latest"],
    "id": 1
  }'
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

**Blocks**

| Method                                 | Parameters                        | Description                                                                                                                                                     |
| -------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `eth_getBlockByNumber`                 | `blockNumber`, `fullTransactions` | Returns block data by number. Set `fullTransactions` to `true` for full tx objects, `false` for tx hashes only.                                                 |
| `eth_getBlockByHash`                   | `blockHash`, `fullTransactions`   | Returns block data by hash.                                                                                                                                     |
| `eth_getBlockTransactionCountByNumber` | `blockNumber`                     | Returns the number of transactions in a block by number.                                                                                                        |
| `eth_getBlockTransactionCountByHash`   | `blockHash`                       | Returns the number of transactions in a block by hash.                                                                                                          |
| `eth_getBlockReceipts`                 | `blockNumberOrTag`                | Returns all transaction receipts for a block. Accepts a block number, block hash, or block tag. (reth extension — not part of the base Ethereum JSON-RPC spec.) |

**Example: `eth_getBlockByNumber`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

Returns the full block header plus an array of transaction hashes (or full transaction objects if the second parameter is `true`).

**Transactions**

| Method                                    | Parameters                   | Description                                                                           |
| ----------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------- |
| `eth_getTransactionByHash`                | `txHash`                     | Returns transaction data by hash, or `null` if not found.                             |
| `eth_getTransactionReceipt`               | `txHash`                     | Returns the receipt for a mined transaction, or `null` if pending/unknown.            |
| `eth_getTransactionByBlockHashAndIndex`   | `blockHash`, `index` (hex)   | Returns a transaction by block hash and position index (e.g., `"0x0"` for the first). |
| `eth_getTransactionByBlockNumberAndIndex` | `blockNumber`, `index` (hex) | Returns a transaction by block number and position index.                             |
| `eth_sendRawTransaction`                  | `signedTxData`               | Submits a signed transaction to the network. Returns the transaction hash.            |

**Example: `eth_sendRawTransaction`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0x02f870..."],
    "id": 1
  }'
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

Replace the params value with your full RLP-encoded signed transaction. The transaction must be signed with a valid chain ID (`3946` for mainnet, `3945` for testnet). EIP-1559 (type 2) transactions are supported. The transaction object accepts `maxFeePerGas` and `maxPriorityFeePerGas` fields for fee specification.

**Logs and Filters**

| Method                            | Parameters     | Description                                                                                           |
| --------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------- |
| `eth_getLogs`                     | `filterObject` | Returns logs matching the filter. Large block ranges may be routed to the archive node automatically. |
| `eth_newFilter`                   | `filterObject` | Creates a log filter, returns a filter ID. Poll with `eth_getFilterChanges`.                          |
| `eth_newBlockFilter`              | —              | Creates a filter for new blocks. Poll with `eth_getFilterChanges`.                                    |
| `eth_newPendingTransactionFilter` | —              | Creates a filter for pending transactions. Poll with `eth_getFilterChanges`.                          |
| `eth_getFilterChanges`            | `filterId`     | Returns new entries since the last poll for the given filter ID.                                      |
| `eth_getFilterLogs`               | `filterId`     | Returns all logs matching the filter (equivalent to `eth_getLogs` for the filter's parameters).       |
| `eth_uninstallFilter`             | `filterId`     | Removes a filter. Filters also expire after a period of inactivity.                                   |

The filter object accepts `fromBlock`, `toBlock`, `address`, and `topics` fields. Keep block ranges small for best performance. For real-time log streaming, prefer WebSocket subscriptions over HTTP filter polling.

**Example: `eth_getLogs`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "0x2c6200",
      "toBlock": "0x2c620e",
      "address": "0x69BFfB3C9cb3aa81E8ab23dad470246956D2cC7F"
    }],
    "id": 1
  }'
```

**Execution**

| Method                 | Parameters                | Description                                                                      |
| ---------------------- | ------------------------- | -------------------------------------------------------------------------------- |
| `eth_call`             | `txObject`, `blockNumber` | Executes a call without creating a transaction. Used for reading contract state. |
| `eth_estimateGas`      | `txObject`, `blockNumber` | Estimates the gas required to execute a transaction.                             |
| `eth_createAccessList` | `txObject`, `blockNumber` | Creates an EIP-2930 access list for a transaction.                               |

**Example: `eth_call`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0x69BFfB3C9cb3aa81E8ab23dad470246956D2cC7F",
      "data": "0x18160ddd"
    }, "latest"],
    "id": 1
  }'
```

`0x18160ddd` is the function selector for `totalSupply()`. Replace `to` with any contract address and `data` with the appropriate ABI-encoded call data.

#### `net` Namespace

| Method          | Description                                                                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `net_version`   | Returns the network ID as a decimal string (`"3946"` for mainnet, `"3945"` for testnet). Note: this returns a decimal string, unlike `eth_chainId` which returns hex. |
| `net_peerCount` | Returns the number of connected peers.                                                                                                                                |
| `net_listening` | Returns `true` if the node is listening for connections.                                                                                                              |

#### `web3` Namespace

| Method               | Description                                                                      |
| -------------------- | -------------------------------------------------------------------------------- |
| `web3_clientVersion` | Returns the client identifier (`reth/v1.10.2-8e3b5e6/x86_64-unknown-linux-gnu`). |
| `web3_sha3`          | Returns the Keccak-256 hash of the given data.                                   |

## WebSocket

WebSocket connections (`wss://mainnet.rpc.nexus.xyz`, `wss://testnet.rpc.nexus.xyz`) require an `X-Api-Key` header on the upgrade handshake.

Since browsers cannot set custom headers on WebSocket connections, WSS is currently limited to server-side use (Node.js, Python, etc.). Browser dApps should use HTTP polling or a server-side relay.

**Subscription Methods:**

| Method            | Description                                |
| ----------------- | ------------------------------------------ |
| `eth_subscribe`   | Opens a subscription for real-time events. |
| `eth_unsubscribe` | Closes an active subscription.             |

**Subscription Types:**

| Type                     | Description                                                                                        |
| ------------------------ | -------------------------------------------------------------------------------------------------- |
| `newHeads`               | Emits a header object each time a new block is produced (\~1 per second).                          |
| `logs`                   | Emits log entries matching a filter in real time. Accepts the same filter object as `eth_getLogs`. |
| `newPendingTransactions` | Emits transaction hashes as they enter the mempool.                                                |

**Example: Subscribe to new blocks (Node.js)**

```javascript
const WebSocket = require("ws");

const ws = new WebSocket("wss://mainnet.rpc.nexus.xyz", {
  headers: { "X-Api-Key": "YOUR_API_KEY" },
});

ws.on("open", () => {
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    method: "eth_subscribe",
    params: ["newHeads"],
    id: 1,
  }));
});

ws.on("message", (data) => {
  const msg = JSON.parse(data);
  if (msg.params) {
    console.log("New block:", parseInt(msg.params.result.number, 16));
  }
});
```

**Example: Subscribe to new blocks (Python)**

```python
import asyncio
import json
import websockets

async def subscribe():
    async with websockets.connect(
        "wss://mainnet.rpc.nexus.xyz",
        additional_headers={"X-Api-Key": "YOUR_API_KEY"},
    ) as ws:
        await ws.send(json.dumps({
            "jsonrpc": "2.0",
            "method": "eth_subscribe",
            "params": ["newHeads"],
            "id": 1,
        }))
        async for message in ws:
            msg = json.loads(message)
            if "params" in msg:
                block_num = int(msg["params"]["result"]["number"], 16)
                print(f"New block: {block_num}")

asyncio.run(subscribe())
```

## Archive & Historical Data

The default RPC endpoint routes to full nodes. Debug, trace, and historical queries route through a separate archive path.

#### Method Routing

| Method category                                                     | Default RPC (`/`)               | Archive (`/archive`) |
| ------------------------------------------------------------------- | ------------------------------- | -------------------- |
| Standard methods (`eth_blockNumber`, `eth_getBalance@latest`, etc.) | Yes                             | No                   |
| `debug_*`                                                           | No (`-32601`)                   | Yes                  |
| `trace_*`                                                           | No (`-32601`)                   | Yes                  |
| `eth_getLogs` with block range                                      | Routed to archive automatically | Yes                  |

#### Archive and Debug Methods

Debug and trace methods are not available on the default RPC endpoint. They are served from a separate archive node at the `/archive` path.

| Method                     | Endpoint   | Description                                                         |
| -------------------------- | ---------- | ------------------------------------------------------------------- |
| `debug_traceTransaction`   | `/archive` | Returns the execution trace of a transaction.                       |
| `debug_traceBlockByNumber` | `/archive` | Returns execution traces for all transactions in a block.           |
| `debug_traceBlockByHash`   | `/archive` | Returns execution traces for all transactions in a block (by hash). |
| `trace_block`              | `/archive` | Returns Parity-style traces for all transactions in a block.        |
| `trace_transaction`        | `/archive` | Returns Parity-style traces for a single transaction.               |
| `trace_replayTransaction`  | `/archive` | Re-executes a transaction and returns the requested trace types.    |

Standard methods (`eth_blockNumber`, `eth_chainId`, etc.) are **rejected** on the `/archive` path. Use the default endpoint for those.

**Example: `debug_traceTransaction`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz/archive \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceTransaction",
    "params": ["0xYOUR_TX_HASH", {"tracer": "callTracer"}],
    "id": 1
  }'
```

The `callTracer` and `prestateTracer` built-in tracers are both supported.

**Example: `trace_block`**

```bash
curl -s -X POST https://mainnet.rpc.nexus.xyz/archive \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_block",
    "params": ["0x2c620e"],
    "id": 1
  }'
```

Pass a hex-encoded block number. Block tags like `"latest"` are not accepted by `trace_block`.

#### Archive endpoint

Append `/archive` to the RPC URL:

```
# Testnet archive
curl -s -X POST https://testnet.rpc.nexus.xyz/archive \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"debug_traceTransaction","params":["<tx_hash>"],"id":1}'
```

```
# Mainnet archive
curl -s -X POST https://mainnet.rpc.nexus.xyz/archive \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"trace_block","params":["latest"],"id":1}'
```

Standard methods (`eth_blockNumber`, `eth_chainId`, etc.) are rejected on the `/archive` path — use the default endpoint for those.

The archive node is not directly addressable. All requests route through the gateway.

### Error Codes

| Code     | Message          | Cause                                                                     |
| -------- | ---------------- | ------------------------------------------------------------------------- |
| `-32700` | Parse error      | Malformed JSON in request body                                            |
| `-32600` | Invalid request  | Missing required JSON-RPC fields                                          |
| `-32601` | Method not found | Method not supported or called on wrong endpoint (e.g., `debug_*` on `/`) |
| `-32602` | Invalid params   | Wrong number or type of parameters                                        |
| `-32603` | Internal error   | Server-side failure                                                       |
| `-32005` | Limit exceeded   | Payload too large or batch size exceeded                                  |


# Nodes

#### Full nodes

Full nodes replicate chain state and serve RPC queries. No staking or proof generation required.

Get in touch with our team if you would like to run your own full node: <growth@nexus.xyz>

#### Provers

Provers generate validity proofs using the Nexus zkVM. The Compute Network is live on testnet and will expand to mainnet proof generation.

#### Validators

The Nexus blockchain is secured by a validator set running NexusBFT consensus. The set is permissioned at launch and will expand over time.


# Overview

A prover is a node in the Compute Network that generates validity proofs of execution. The Compute Network is live on testnet. Mainnet proof generation will launch alongside the Nexus Exchange.

To run a prover on testnet today, see [Proving via CLI](/network/nodes/proving-via-cli).

#### What provers will prove

* Exchange execution — order matching, positions, risk, liquidations (NexusCore, 200 ms blocks)
* Smart contract execution — EVM transactions (NexusEVM, 1s blocks)

Proof coverage will expand incrementally. Initial scope and rollout schedule will be published before mainnet proof generation begins.

#### Requirements

Hardware, network, and software requirements will be published when benchmarks are finalized. Proof generation is computationally intensive — expect requirements significantly higher than full node minimums.

#### Operational expectations

* **Availability** — must be online and ready to accept proof tasks
* **Task completion** — assigned tasks must be completed within the required time window
* **Monitoring** — operators should monitor performance, disk space, and connectivity
* **Upgrades** — prover software must be kept up to date

Provers that fail to meet these expectations will face slashing. Staking requirements and slashing parameters are not yet finalized.


# Proving via CLI

#### Introduction <a href="#introduction" id="introduction"></a>

The [Nexus Network CLI](https://github.com/nexus-xyz/nexus-cli) is a command-line tool for contributing compute resources to the network.

#### Install Script <a href="#install-script" id="install-script"></a>

Quick install (scripted):

```
curl https://cli.nexus.xyz/compute | sh
```

> The bare `cli.nexus.xyz` one-liner now installs the Nexus **Exchange** CLI. The compute/prover CLI installs from the `cli.nexus.xyz/compute` path shown above. Existing installs keep working — the `nexus-network` command is unchanged.

After installing, restart or refresh your terminal (e.g. `source ~/.bashrc`, `source ~/.zshrc`, etc.). To start with an existing node ID:

```
nexus-network start --node-id <your-node-id>
```

Alternatively, register your wallet address and create a node ID with the CLI, or at app.nexus.xyz:

```
nexus-network register-user --wallet-address <your-wallet-address>
nexus-network register-node
nexus-network start
```

The `register-user` and `register-node` commands will save your credentials to `~/.nexus/credentials.json`. To clear credentials, run:

```
nexus-network logout
```


# Tooling and Infrastructure

#### Development toolchains

| Tool                                                   | Status                                                     |
| ------------------------------------------------------ | ---------------------------------------------------------- |
| [Foundry](https://book.getfoundry.sh/)                 | Supported — forge, cast, anvil                             |
| [Hardhat](https://hardhat.org/)                        | Supported                                                  |
| [Remix](https://remix.ethereum.org/)                   | Supported via Injected Provider                            |
| [OpenZeppelin](https://www.openzeppelin.com/contracts) | Supported — ERC-20, ERC-721, ERC-1155, proxies, governance |

#### RPC providers

| Provider                                       | Mainnet                                                                                                                    | Testnet                         | Notes                |
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------------------- |
| **Nexus (official)**                           | <p><code><https://mainnet.rpc.nexus.xyz></code><br><br><code>wss\://mainnet.rpc.nexus.xyz</code></p>                       | `https://testnet.rpc.nexus.xyz` | —                    |
| [Dteam](https://dteam.tech/)                   | <p><code><https://evm-rpc.nexus.mainnet.dteam.tech></code><br><br><code>wss\://evm-rpc.nexus.mainnet.dteam.tech</code></p> | —                               | Rate-limit: 1000 RPS |
| [ThirdWeb](https://thirdweb.com/nexus-mainnet) | `https://3946.rpc.thirdweb.com`                                                                                            | —                               | —                    |

#### Wallets

| Wallet         | Type                       | Notes                                                                              |
| -------------- | -------------------------- | ---------------------------------------------------------------------------------- |
| MetaMask       | Browser extension / mobile | Add network with chain ID 3946 (setup guide)                                       |
| Any EVM wallet | —                          | WalletConnect, Rabby, Coinbase Wallet — any wallet that supports custom EVM chains |

#### Bridges

| Bridge                                                                                                            | Route          | Link                                                                                                |
| ----------------------------------------------------------------------------------------------------------------- | -------------- | --------------------------------------------------------------------------------------------------- |
| [Hyperlane](https://nexus.hyperlane.xyz/?origin=ethereum\&originToken=NEX\&destination=bsc\&destinationToken=NEX) | Ethereum ↔ BSC | <https://nexus.hyperlane.xyz/?origin=ethereum&originToken=NEX&destination=bsc&destinationToken=NEX> |

#### SAFE Multi-sig

| Provider | Link                                  | Notes                                        |
| -------- | ------------------------------------- | -------------------------------------------- |
| Palmera  | <https://safe.palmeradao.xyz/welcome> | UI and Backend for SAFE Multi-sig management |

#### Block explorers

| Explorer       | URL                          |
| -------------- | ---------------------------- |
| Nexus Explorer | <https://explorer.nexus.xyz> |

#### Libraries and SDKs

| Library                                                    | Language              | Notes     |
| ---------------------------------------------------------- | --------------------- | --------- |
| [ethers.js](https://docs.ethers.org/)                      | TypeScript/JavaScript | Supported |
| [viem](https://viem.sh/)                                   | TypeScript            | Supported |
| [web3.py](https://web3py.readthedocs.io/)                  | Python                | Supported |
| [Foundry cast](https://book.getfoundry.sh/reference/cast/) | CLI                   | Supported |


# Security Audits

Independent security reviews of Nexus components. The full reports are published on the [Nexus Security Audits page](https://docs.nexus.xyz/extra/security-audits) and can be downloaded there; each is listed below.

## NEX

Smart-contract audit of the NEX token and bridge contracts.

{% file src="/files/UNdFA8VldeOHmKg722WL" %}

## Nexus zkVM

Hybrid application security assessment of the Nexus zkVM (2025), with the follow-up remediation report.

{% file src="/files/LS1otMwjVtOjPodemAl1" %}

{% file src="/files/QyVyWYbxdBKMOew6swJL" %}


# The Nexus zkVM

![](/files/HE65vXy8PApI1XT5JYiK)

The Nexus zero-knowledge virtual machine is a modular, extensible, prover-optimized, fully-specified zkVM written in Rust, focused on performance and security. [Nexus zkVM v0.3.6](https://github.com/nexus-xyz/nexus-zkvm/releases/tag/v0.3.6) is the current stable release, implementing the [Nexus zkVM 3.0 machine](/zkvm/specifications/the-complete-specification).

## What is the Nexus zkVM?

Nexus zkVM is a [zero-knowledge virtual machine](/zkvm/overview/zkvm-overview) that enables developers to generate succinct proofs for any computation, demonstrating that a program executed every instruction and accessed memory correctly to produce a given output. Built with Rust and focused on performance and security, it provides a robust foundation for building applications.

## Proving Computation

The Nexus zkVM can generate proofs for any computation. For example, given a Rust program that calculates the Fibonacci sequence:

{% code title="fib.rs" %}

```rust
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]

use nexus_rt::println;

fn fib(n: u32) -> u32 {
    match n {
        0 => 0,
        1 => 1,
        _ => fib(n - 1) + fib(n - 2),
    }
}

#[nexus_rt::main]
fn main() {
    let n = 7;
    let result = fib(n);
    assert_eq!(result, 13);
    println!("fib({}) = {}", n, result);
}
```

{% endcode %}

the zkVM can generate a succinct, efficiently verifiable proof of its correct execution. To get started with the Nexus zkVM, check out the [Getting Started](/zkvm/development/getting-started) page.

{% hint style="warning" %}
The Nexus zkVM is in an experimental stage and is not currently recommended for production use.
{% endhint %}

## Key Features

### Security First

* The Nexus zkVM provides fully-specified cryptographic components with careful analysis of security and performance.
* The system includes no code obfuscation, no proprietary components, and no closed-source code.
* Source-available transparency ensures complete auditability for all users.

### Performance Optimized

* The prover-optimized architecture enables efficient proof generation across diverse workloads.
* Sensible defaults are configured to work out of the box without complex setup requirements.

### Developer Friendly

* Developers can write programs with execution proven to be correct.
* A comprehensive SDK and tooling ecosystem supports rapid development and deployment.
* Extensive documentation and examples guide users through implementation details.

### Extensible & Modular

* The system supports new languages, new precompiles, and new provers as the state-of-the-art advances.
* Configurable components prevent vendor lock-in and allow customization for specific use cases.
* Source-available code and consistent development by the Nexus zkVM team ensure long-term reliability.

## The Nexus Ethos: Assurance through Open Science

We believe a zkVM must provide an efficient proving mechanism without compromising on security and correctness. A zkVM cannot provide transparency without being transparent itself. Every component of a zkVM should be powered by fully and publicly specified cryptographic components, with careful analysis of security and performance. The Nexus zkVM features no code obfuscation, no proprietary components, and no closed-source code.

## Modular and Extensible Architecture

Built with a modular architecture at its core, the Nexus zkVM features independently optimized components that integrate smoothly. The system ships with carefully chosen defaults for the prover and memory model, allowing developers to start building immediately while maintaining confidence in both security and performance across diverse applications.

Extensibility is a fundamental design principle of the Nexus zkVM. The codebase and active development by the Nexus team ensures continuous evolution, supporting emerging languages, advanced precompiles, and cutting-edge proving systems as the field progresses—all without locking developers into proprietary solutions.

## Use Cases

The Nexus zkVM enables a wide range of applications:

* **Verifiable Computation**: Prove correct execution of any program
* **Privacy-Preserving Applications**: Compute on sensitive data without revealing it
* **Blockchain Scaling**: Generate proofs for off-chain computation
* **Compliance & Auditing**: Provide cryptographic guarantees for regulatory requirements
* **Trustless Systems**: Build applications that don’t require trusted intermediaries

## Getting Started

{% stepper %}
{% step %}

### Quick Start Guide

Get up and running in minutes: <https://docs.nexus.xyz/zkvm/proving/overview>
{% endstep %}

{% step %}

### SDK Documentation

Comprehensive API reference: <https://docs.nexus.xyz/zkvm/proving/sdk>
{% endstep %}

{% step %}

### Architecture Overview

Understand how it works: <https://docs.nexus.xyz/zkvm/architecture>
{% endstep %}

{% step %}

### Example Walkthroughs

Learn through practical examples: <https://docs.nexus.xyz/zkvm/walkthroughs/galeshapley>
{% endstep %}
{% endstepper %}

***

*Experience the future of verifiable computation with Nexus zkVM - where transparency meets performance.*


# zkVM Overview

Zero-Knowledge Virtual Machines (zkVMs) represent an approach to verifiable computation, enabling developers to generate cryptographic proofs that a computation was executed correctly.

## What is a zkVM?

A zero-knowledge virtual machine is a system that can execute programs and generate succinct, verifiable proofs of their correct execution. These proofs demonstrate that a program executed every line of code and accessed memory correctly, culminating in the expected output. Unlike traditional virtual machines that simply run code, zkVMs produce cryptographic evidence that the computation was performed accurately according to the specified program logic.

This fundamental capability transforms how we approach trust in computing. Instead of relying on individual parties or infrastructure to validate computations, zkVMs enable any computation execution to be independently verified. This opens up entirely new possibilities for preserving privacy and ensuring computational integrity across distributed networks.

## Key Concepts

### Zero-Knowledge Proofs

Succinct proofs of correct execution are cryptographic proofs that allow a prover to convince a verifier that a certain computation was performed correctly — without the verifier having to redo the computation themselves. These proofs are typically non-interactive, publicly verifiable, and much smaller and faster to verify than re-executing the original computation.

A zero-knowledge proof is a special type of such proof that reveals nothing beyond the correctness of the statement itself. That is, it allows the verifier to be convinced that the computation was carried out correctly, without learning anything about the inputs, intermediate steps, or any other private data.

Example: For an example of a zero-knowledge proof, see this description of Schnorr’s: <https://www.zkdocs.com/docs/zkdocs/zero-knowledge-protocols/schnorr/>

### Succinct Verification

zkVM proofs are “succinct,” meaning they can be verified much faster than re-executing the original computation. A proof for a program that takes hours to run can often be verified in milliseconds.

### Universal Computation

Modern zkVMs are designed to handle arbitrary computations, not just specific mathematical operations. This means developers can write programs in familiar languages and generate proofs for complex business logic.

## How zkVMs Work

{% stepper %}
{% step %}

### Program Execution

The zkVM executes a program step-by-step, tracking all operations and state changes and recording the computational trace.
{% endstep %}

{% step %}

### Proof Generation

The trace is converted into a cryptographic proof using advanced techniques like STARKs, SNARKs, or other proof systems.
{% endstep %}

{% step %}

### Verification

Anyone can verify the proof to confirm the correctness of the trace is proven.
{% endstep %}
{% endstepper %}

## Applications of zkVMs

### Privacy-Preserving Computation

Sensitive computations can be proven correct without revealing private inputs, enabling applications in healthcare, finance, and personal data processing. See walkthroughs at Gale-Shapley and Lambda Calculus:

* <https://docs.nexus.xyz/zkvm/walkthroughs/galeshapley>
* <https://docs.nexus.xyz/zkvm/walkthroughs/lambdacalculus>

### Verifiable AI/ML

Machine learning models can generate proofs of their inference results, ensuring AI decisions are transparent and auditable.

### Compliance and Auditing

Organizations can prove compliance with regulations or internal policies without exposing sensitive business data.

### Blockchain Scaling

zkVMs enable Layer 2 scaling solutions by allowing complex computations to be performed off-chain while maintaining on-chain verifiability.

***

*Zero-knowledge virtual machines are transforming how we think about computation, privacy, and trust in digital systems.*

Further reading:

* The Nexus zkVM: <https://docs.nexus.xyz/zkvm/nexus-zkvm>
* Architecture: <https://docs.nexus.xyz/zkvm/architecture>


# Architecture

The ethos of the Nexus project is a commitment to open and transparent science and engineering. For an in-depth look at the science behind the Nexus zkVM, see the [specification](/zkvm/specifications/the-complete-specification).

### Core Components

The Nexus zkVM features a modular and extensible architecture built from highly optimized components:

* **The Nexus Runtime**: A feature-rich runtime environment that streamlines guest program development for the Nexus zkVM, with full support for public and private inputs, public outputs, logging, and performance benchmarking — all in native Rust syntax.
* **The Nexus zkVM Machine Architecture**: A custom designed and from-scratch implemented and purpose-built RISC-V virtual machine in a modified Harvard architecture, designed to optimize prover performance through careful memory management in support of a “only prove what you use” model.
* **The Nexus zkVM**: A fully-specified Algebraic Intermediate Representation (AIR) arithmetization of the machine architecture, featuring comprehensive constraints for the full RISC-V32im instruction set alongside efficient offline memory checking.
* **The Stwo Prover:** An integration with StarkWare’s powerful [Stwo prover](https://github.com/starkware-libs/stwo) (which has since rebranded from Stwo to S-two), a state-of-the-art Circle STARK with excellent performance characteristics.

Every component within the Nexus zkVM has been carefully chosen or designed from scratch by the [Nexus team](https://nexus.xyz/aboutus) to maximize security, performance, modularity, and extensibility.

As a consequence of the architectural foundation, the Nexus zkVM is architected to support new theoretical developments led by our research team, as well as tooling to accelerate deploying the zkVM for a variety of use cases:

* **Proving Schemes**: Beyond the Stwo prover, the Nexus zkVM maintains compatibility with the Nova family of folding schemes and supports integration of emerging proving constructions as cryptographic research advances.
* **Precompiles**: Precompiles are extensions to the instruction set of the machine architecture, supporting common operations (e.g., cryptographic hashing like Keccak, or matrix multiplication) that the zkVM can use to accelerate specific computations. Developers will be able to extend the zkVM with their own custom precompiles, as well as import those published by other developers.
* **Developer Tooling:** The Nexus zkVM comes with a comprehensive SDK that streamlines application development with APIs that enable efficient development workflows regardless of use case complexity.
* **Language Support**: As the Nexus zkVM implements RISC-V ISA, the zkVM can run programs written in most high-level languages (e.g., Rust, C++, etc.) given its common availability as a computation target.

The zkVM aims to offer developers out-of-the-box prover performance and security, designed to power numerous applications.

### Proving Architectures

The Nexus zkVM turns programs into proofs, but the computational work of executing and proving the zkVM must be implemented by a proving architecture. The Nexus zkVM’s flexible design supports diverse proving architectures, ranging from local sequential execution on personal devices to the massively parallel [Nexus Network](https://docs.nexus.xyz/layer-1/vision/network), a globally-distributed proving infrastructure currently in active development.

Related:

* [zkVM Overview](/zkvm/overview/zkvm-overview)
* [Getting Started](/zkvm/development/getting-started)


# Getting Started

The Nexus zkVM provides both a comprehensive runtime for streamlined program development and a powerful SDK for configuring, executing, and generating proofs for your applications.

{% hint style="info" %}
Recommended starting points:

* SDK Quick Start — step-by-step guidance to begin proving with the Nexus zkVM.
* Runtime — guidance for writing guest programs for the zkVM and developing host programs to manage the proving process.
  {% endhint %}

## Key resources

* <a href="/pages/b513e7019934931e4eb130a2a505ee8de62ff8cd" class="button primary">SDK Quick Start</a>
* <a href="/pages/e75c829d318bf54f601847cafcef10908d241a05" class="button primary">Runtime</a>

Additional references:

* SDK Documentation: <https://docs.nexus.xyz/zkvm/proving/sdk-docs>
* Benchmarking your host project: <https://docs.nexus.xyz/zkvm/proving/sdk-benchmarking>
* Integrating Precompiles: <https://docs.nexus.xyz/zkvm/proving/precompiles>

(Links preserved with original query parameters and targets.)


# SDK Quick Start

The Nexus SDK provides simple, misuse-resistant programmatic use of the Nexus zkVM.

{% stepper %}
{% step %}

### Install the Nexus zkVM

First, install Rust: <https://www.rust-lang.org/tools/install>. Next, install the RISC-V target:

{% code title="Install RISC-V target" %}

```bash
$ rustup target add riscv32i-unknown-none-elf
```

{% endcode %}

Then, install the Nexus zkVM:

{% code title="Install cargo-nexus (v0.3.4)" %}

```bash
$ rustup run nightly-2025-05-09 cargo install --git https://github.com/nexus-xyz/nexus-zkvm cargo-nexus --tag 'v0.3.6'
```

{% endcode %}

And verify the installation:

{% code title="Verify installation" %}

```bash
$ rustup run nightly-2025-05-09 cargo nexus --help
```

{% endcode %}

This should print the available CLI commands. At present, the `cargo nexus` CLI is minimal, providing just a `cargo nexus host` command to setup an SDK based project.
{% endstep %}

{% step %}

### Create a new Nexus host project

To use the zkVM programmatically, you need two programs: a guest program that runs on the zkVM, and a host program that operates the zkVM. Create the project with:

{% code title="Generate host project" %}

```bash
$ rustup run nightly-2025-05-09 cargo nexus host nexus-host
```

{% endcode %}

This will create a new Rust project directory with the following structure:

{% code title="Project structure" %}

```
./nexus-host
├── Cargo.lock
├── Cargo.toml
├── rust-toolchain.toml
└── src
    ├── main.rs
    └── guest
        ├── Cargo.toml
        ├── rust-toolchain.toml
        └── src
            └── main.rs
```

{% endcode %}

Here, `./src/main.rs` is the host program, while `./src/guest/src/main.rs` is the guest program.

Replace `./src/guest/src/main.rs` with this guest program (takes two integers, one public and one private, logs values, returns their product):

{% code title="src/guest/src/main.rs" %}

```rust
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]

use nexus_rt::println;

#[nexus_rt::main]
#[nexus_rt::public_input(x)]
fn main(x: u32, y: u32) -> u32 {
    println!("Read public input:  {}", x);
    println!("Read private input: {}", y);

    x * y
}
```

{% endcode %}

Then replace `./src/main.rs` with this host program (compiles guest, invokes it with public input x = 5 and private input y = 3, reads output and logs, and verifies the proof):

{% code title="src/main.rs" %}

```rust
use nexus_sdk::{
    compile::{cargo::CargoPackager, Compile, Compiler},
    stwo::seq::Stwo,
    ByGuestCompilation, Local, Prover, Verifiable, Viewable,
};

const PACKAGE: &str = "guest";

fn main() {
    println!("Compiling guest program...");
    let mut prover_compiler = Compiler::<CargoPackager>::new(PACKAGE);
    let prover: Stwo<Local> =
        Stwo::compile(&mut prover_compiler).expect("failed to compile guest program");

    let elf = prover.elf.clone(); // save elf for use with test verification

    print!("Proving execution of vm... ");
    let (view, proof) = prover
        .prove_with_input::<u32, u32>(&3, &5)
        .expect("failed to prove program"); // x = 5, y = 3

    assert_eq!(view.exit_code().expect("failed to retrieve exit code"), nexus_sdk::KnownExitCodes::EXIT_SUCCESS as u32);

    let output: u32 = view
        .public_output::<u32>()
        .expect("failed to retrieve public output");
    assert_eq!(output, 15); // z = 15

    println!("output is {}!", output);
    println!(
        ">>>>> Logging\n{}<<<<<",
        view.logs().expect("failed to retrieve debug logs").join("")
    );

    print!("Verifying execution...");
    proof
        .verify_expected::<u32, u32>(
            &5,   // x = 5
            nexus_sdk::KnownExitCodes::EXIT_SUCCESS as u32,
            &15,  // z = 15
            &elf, // expected elf (program binary)
            &[],  // no associated data,
        )
        .expect("failed to verify proof");

    println!("  Succeeded!");
}
```

{% endcode %}

This host program compiles the guest, runs the zkVM with supplied inputs, retrieves output and logs, and verifies the proof of correct execution.
{% endstep %}

{% step %}

### Run your program

Run the host program (which executes and proves the guest program) with:

{% code title="Run host" %}

```bash
$ cargo run -r
```

{% endcode %}

You should see output similar to:

{% code title="Expected output" %}

```
Proving execution of vm... output is 15!
>>>>> Logging
Read public input:  5
Read private input: 3
<<<<<
Verifying execution...  Succeeded!
```

{% endcode %}

For more examples using the SDK with more complicated guest programs, see the walkthroughs for Gale-Shapley stable matching and the lambda calculus:

* <https://docs.nexus.xyz/zkvm/walkthroughs/galeshapley>
* <https://docs.nexus.xyz/zkvm/walkthroughs/lambdacalculus>
  {% endstep %}

{% step %}

### Run in legacy mode

In addition to the Stwo-based Nexus zkVM 3.0 prover, the SDK supports a legacy mode that uses the Nova, HyperNova, and (experimental) Jolt-based Nexus zkVM 2.0 machine. This machine uses a different runtime and requires additional host-side configuration due to public parameters and reference strings.

To use legacy mode, activate the appropriate feature for the `nexus-sdk` dependency in the host program: `legacy-nova`, `legacy-hypernova`, or `legacy-jolt`. Examples of using legacy mode to prove legacy guest programs are provided in the examples folder on GitHub:

* Legacy guest examples: <https://github.com/nexus-xyz/nexus-zkvm/tree/main/examples/legacy/src>
* SDK examples: <https://github.com/nexus-xyz/nexus-zkvm/tree/main/sdk/examples>

The legacy-mode code corresponds to the Nexus zkVM v0.2.4 release:

* <https://github.com/nexus-xyz/nexus-zkvm/tree/releases/0.2.4>
  {% endstep %}
  {% endstepper %}


# SDK Documentation

The Nexus zkVM SDK documentation is available at:

* <https://sdk-docs.nexus.xyz/doc/nexus_sdk/index.html>


# Runtime

## What’s a Runtime?

In the vast majority of modern software development, a developer never writes code that directly interacts with the platform it’s running on. Instead, the platforms for which developers write code offer runtimes: easy-to-use (and often standardized) APIs that provide high-level functionality while abstracting away most platform-specific details. Prominent examples include POSIX and `glibc`. Runtimes also provide controlled and abstracted access to platform-specific functionality that developers do need access to, which is especially relevant for the Nexus zkVM.

## The Nexus Runtime

The zkVM runtime environment is most similar to a bare-metal or embedded environment, though it has properties specific to being a zkVM. As is typical for embedded platforms, interaction with the platform (zkVM) is done via raw `ecall`s, custom instructions, and memory-mapped I/O. Uniquely to zkVMs, however, [the memory model changes fundamentally between executions of the same guest program](/zkvm/overview/architecture) (see the aside below). The Nexus runtime is a set of libraries and macros that allows developers to write higher-level programs that can run efficiently and correctly on the Nexus zkVM, agnostic to the subtleties of the zkVM’s varying executions.

{% hint style="info" %}
Aside: the zkVM’s first pass uses a Harvard-like architecture with distinct memory spaces for code, data, inputs, and output. During the first pass, the zkVM collects statistics about exactly how much memory is used in each of these spaces. Before the second pass, the zkVM lays out the program’s memory in a single address space whose size is minimized based on the statistics collected during the first pass. Because proving memory is expensive, this process significantly reduces proof sizes and proving times. The second pass maintains memory protections but uses a single address space for all memory segments.
{% endhint %}

More specifically, the Nexus runtime consists of a few parts:

* Macros that allow developers to work easily with input and output, which are memory-mapped and directly managed by the zkVM.
* A `main` macro that ensures the guest program’s `main` function can be correctly located and run by the zkVM.
* `print!` / `println!` macros that write to an output log, implemented by the zkVM via an `ecall`.
* A `#[panic_handler]`, required by Rust’s `core`, implemented by the zkVM via an `ecall`.
* A `#[global_allocator]` that operates correctly across the zkVM’s memory models.
* An assembly entry point for the program that configures zkVM-specific global state and calls the guest program’s `main` function; this ensures stack, heap, and input/output segments are usable during both passes of execution.
* Well-known locations that correspond to particular memory-mapped values provided or read by the zkVM.

The rest of this section discusses these components in more detail and with examples. For even more detail, see [the VM specification](/zkvm/specifications/the-complete-specification).

### Notable Macros & Functions

#### Main

The `nexus_rt::main` attribute marks the entry point of the guest program. It is a procedural macro that:

* Ensures that the main function can be located and run by the runtime’s entry-point assembly by re-exporting the main function with a non-mangled, well-known name referenced by the runtime’s entry-point assembly.
* Generates code that automatically reads in all inputs and writes to the output. Unless specified otherwise, inputs default to private.
* Re-orders the function’s attributes to ensure that input type specifications are evaluated before the `main` macro generates code that reads them.

Example use:

```rust
#[nexus_rt::main]
fn main(x: u32, y: u32) -> bool {
    x > y
}
```

The `main` macro also handles the program’s public output, which is simply the value returned from `main`. The macro internally uses the `write_public_output` function to serialize the output and write it to the output tape. The public output is written by the Nexus runtime’s `write_public_output`, which is a function that serializes the program’s output in a [COBS](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing) representation [generated by](https://docs.rs/postcard/latest/postcard/fn.to_allocvec_cobs.html) [`postcard`](https://crates.io/crates/postcard).

The serialized bytes are written to the output tape using the Nexus runtime’s `write_output!(byte_index, value)` macro, which writes a byte-addressed word to the output tape by:

{% stepper %}
{% step %}
Read the public output address from well-known location `PUBLIC_OUTPUT_ADDRESS_LOCATION`.
{% endstep %}

{% step %}
Add `byte_index` to that address.
{% endstep %}

{% step %}
Emit a `wou` instruction, whose behavior depends on zkVM execution mode:

* In the zkVM’s first pass (Harvard-like architecture), `wou` writes the word to a unique memory address space that only contains public output.
* In the second pass, the zkVM replaces the binary’s `wou` instructions with ordinary `sw` instructions that write the output values from a location calculated and populated by the zkVM using data collected from the first pass.
  {% endstep %}
  {% endstepper %}

#### Public Input

Public inputs are input values known to the verifier, specified as arguments to `#[nexus_rt::main]` and tagged with `#[nexus_rt::public_input]`.

Example use:

```rust
#[nexus_rt::main]
#[nexus_rt::public_input(x)]
fn main(x: u32, y: u32) -> bool {
    x > y
}
```

In this example, `x` is a public input while `y` is private.

Public inputs are read by the Nexus runtime’s `read_public_input`, which reads the entire public input tape and [deserializes](https://docs.rs/postcard/latest/postcard/fn.from_bytes_cobs.html) it from a [COBS](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing) representation [produced by](https://docs.rs/postcard/latest/postcard/fn.to_allocvec_cobs.html) [`postcard`](https://crates.io/crates/postcard).

The serialized bytes are read using the Nexus runtime’s `read_input!(byte_index)` macro, which reads a byte-addressed word from the serialized public input by:

{% stepper %}
{% step %}
Read the public input address from well-known location `PUBLIC_INPUT_ADDRESS_LOCATION`.
{% endstep %}

{% step %}
Add `byte_index` to that address.
{% endstep %}

{% step %}
Emit a `rin` instruction, whose behavior depends on zkVM execution mode:

* In the zkVM’s first pass (Harvard-like architecture), `rin` reads the word from a unique memory address space which only contains public input.
* In the second pass, the zkVM replaces the binary’s `rin` instructions with ordinary `lw` instructions that read the input values from a location calculated and populated by the zkVM using data collected from the first pass.
  {% endstep %}
  {% endstepper %}

#### Private Input

Private inputs are input values known only to the prover. They are specified as arguments to `#[nexus_rt::main]` and optionally tagged with `#[nexus_rt::private_input]`. Arguments default to being private, but developers can explicitly mark them private for clarity.

Example use:

```rust
#[nexus_rt::main]
#[nexus_rt::private_input(y)]
fn main(x: u32, y: u32) -> bool {
    x > y
}
```

In this example, both `x` and `y` are private inputs.

Private inputs are read by the Nexus runtime’s `read_private_input`, which sequentially reads a single byte from the private input tape via an `ecall`, returning `None` after each byte has been read once. The runtime provides no further special handling for private inputs; guest program developers are expected to handle the interpretation of the private input tape themselves.

#### Host-Native Execution

It is often useful to run and debug guest programs in a native environment (e.g., `cargo run`). The Nexus runtime itself cannot run outside the zkVM, but its macros support native handlers—functions compiled and executed only when the guest program is run natively on the host. These functions generate inputs and handle outputs without the zkVM’s presence.

Example:

```rust
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]

#[cfg(not(target_arch = "riscv32"))]
fn native_input_handler() -> Option<u32> {
    let mut input = String::new();

    std::io::stdin().read_line(&mut input).ok()?;

    Some(input.trim().parse().ok()?)
}

#[cfg(not(target_arch = "riscv32"))]
fn native_output_handler(output: &u32) -> Option<()> {
    println!("Output: {}", output);

    Some(())
}

#[nexus_rt::main]
#[cfg_attr(target_arch = "riscv32", nexus_rt::public_input(x))]
#[cfg_attr(not(target_arch = "riscv32"), nexus_rt::custom_input(x, native_input_handler))]
#[cfg_attr(not(target_arch = "riscv32"), nexus_rt::custom_input(y, native_input_handler))]
#[cfg_attr(not(target_arch = "riscv32"), nexus_rt::custom_output(native_output_handler))]
fn main(x: u32, y: u32) -> u32 {
    x ^ 0xdeadbeef
}
```

This allows running the program natively to ensure correctness without relying on the zkVM’s limited debugging capabilities.

#### Functionality in a `no_std` Environment

The Nexus zkVM has no operating system and cannot implement large portions of the Rust standard library, so guest programs must be `no_std`. To help developers, the Nexus runtime implements particular parts of the Rust runtime:

* A simple global allocator and implementations for `panic` and `abort`, as required by `core`.
* Simple `print!` and `println!` implementations that wrap zkVM-specific system calls for logging.

In the future, the team plans to enable `std` for the Nexus zkVM where appropriate by gating functionality dependent on an operating system or incompatible with provable computation. For example, there are no plans to enable multi-threading or general I/O, but certain `std` functionality useful for the zkVM (e.g., some system calls like `exit`, algorithms, and data structures) may be supported.

### Memory Allocation

The Nexus runtime implements a simple allocator required for any data structures that rely on heap allocations (i.e., those in `alloc`). Currently, it uses a naive bump allocator that allocates bottom-up and never deallocates.

## Assumptions

Despite the conveniences offered by the Nexus runtime, there are still some requirements for guest programs:

* The guest program must depend on `nexus-rt` and provide a `nexus_rt::main`-decorated function as the entry point. Without these, the zkVM can’t properly load the program and behavior is undefined.
* The guest program must be annotated with `no_std` and `no_main` when compiled for the zkVM (i.e., for a `riscv32` target architecture).
* The guest program’s code must be position-independent (compiled with `-fPIC`).

The Nexus `cargo` CLI tool ensures these requirements are met for projects it creates; initializing projects with it is highly recommended.

Further reading:

* [SDK Documentation](broken://pages/4e805822d78f1e65c696c75225e9afcd7b1e7dd6)
* [Precompiles](/zkvm/development/precompiles)


# Precompiles

## Introduction

**Precompiles** are custom extensions to the zkVM’s instruction set that accelerate complex operations most efficiently proved by custom circuits.

As a concrete example, consider [Keccak](https://keccak.team/keccak_specs_summary.html), which implements SHA-3 and is used prominently in some blockchains. The Keccak family of hash functions is optimized for performance on real CPUs—they make extensive use of bitwise operations that conventional CPUs can perform efficiently and in parallel. For current zkVM backends, however, proving a round of a Keccak hash function as a sequence of assembly instructions is quite expensive—that sequence of assembly instructions is long, and bitwise operations are each individually complex to express and expensive to constrain. Using a precompile, we can specifically design a monolithic, optimized circuit for proving a round of a Keccak hash function, potentially saving orders of magnitude in proving time for that operation. As it turns out, many common cryptographic and mathematical operations lend themselves to this kind of optimization.

Actually integrating precompiles into the zkVM is a complex task, however. Guest programs need to be able to use precompiles as if they were standard Rust libraries, and the VM needs to be able to seamlessly provide precompile evaluations to the guest and constraints to the prover. The rest of this documentation is concerned with how the Nexus zkVM actually achieves this.

## Architecture

Above all, the Nexus zkVM is designed to make guest program development as easy as possible. This means that, from the perspective of a developer writing a guest program, using precompiles should be just as easy as using any other Rust library. Using Keccak256 as a concrete example, we want developers to be able to write code like the following:

```rust
use_precompile!(nexus_keccak::Keccak256);

#[nexus_rt::public_input(x)]
#[nexus_rt::main]
fn main(x: &[u8]) -> [u8; 32] {
    Keccak256::hash(x)
}
```

Save for using the `use_precompile!` macro instead of the standard `use` statement, the guest program looks exactly as it would if it were using a standard Rust library. The guest program’s author needs no knowledge whatsoever about how precompiles work to use them correctly and efficiently.

This naturally requires trade-offs. Here, the precompile’s developer takes on the burden of ensuring that their precompile is able to provide a usable interface to the precompile’s functionality. The zkVM tooling provides a set of macros and library functions that make this as easy as possible for precompile developers, but precompile development is a much more advanced task than guest program development.

Consequently, the rest of this document primarily targets precompile developers and advanced users.

### Precompile Instructions

The atom of the Nexus zkVM is the RISC-V `RV32` assembly instruction, each of which the zkVM emulates and proves. A primary characteristic of a modern assembly language like RISC-V is that its instruction set is simple and minimal, making extensions to the instruction set something that needs to be approached carefully.

Fortunately, custom instructions are common, and RISC-V makes provisions for them. Specifically, there are two ways that the RISC-V ISA is designed to be extensible: syscalls (via `ecall`) and custom instructions with reserved opcodes.

We chose to use reserved custom instructions for precompiles. This is primarily because, conceptually, precompiles are *not* syscalls; their purpose is not to interact with the host environment in any particular way. Instead, they are simply a way to extend the functionality of the zkVM, which matches the intended use of reserved custom instructions by the RISC-V specification. These custom instructions make up the `RV32Nexus` extension to the `RV32` ISA, which, in addition to supporting third-party precompiles, will include a set of vetted and optimized first-party precompiles that address common use-cases.

This approach, however, creates some issues. System calls are fundamentally dynamic—one specifies desired behavior by setting register values, and there is only a single `ecall` instruction. As we use them, though, custom instructions, which are static in nature, also need to be dynamic in practice; we cannot simply reserve an instruction for every precompile in our ecosystem. Were we to do that, our ecosystem could only tolerate a small, fixed number of precompiles (1024, here), and developers would need to waste time best spent writing code and circuits fighting for a slot in the instruction set.

Our solution to this problem is multi-fold. At the moment, we have constrained Nexus precompiles to a single opcode (`0x0B`) reserved by the RISC-V specification—technically, this opcode is only reserved by the RISC-V specification for the 32- and 64-bit instruction sets, but, fortunately, Nexus has no plans to ever develop a 128-bit-addressed zkVM. We chose for instructions using this opcode to be R-type. A precompile instruction, then, looks like the following:

```
|--fn7--|-rs2-|-rs1-|fn3|-rd--|0001011|
 31---25       19-15     11--7
         24-20       14        6-----0
                     -12
```

| Our Use                    | `opcode` ( `inst[6:0]`) | `rd`         | `rs1`         | `rs2`         | `imm` | `fn`                                        |
| -------------------------- | ----------------------- | ------------ | ------------- | ------------- | ----- | ------------------------------------------- |
| Dynamic R-type Precompiles | `0001011`               | `inst[11:7]` | `inst[19:15]` | `inst[24:20]` | N/A   | `fn3 = inst[14:12]` and `fn7 = inst[31:25]` |

We generate concrete instructions via a procedural macro, which, for each precompile call, ultimately generates a corresponding custom instruction roughly as follows:

```rust
fn precompile_call<const FN3: u8, const FN7: u8>(rs1: u32, rs2: u32) -> u32 {
    let insn = format!(
        ".insn r 0x{R_TYPE_PRECOMPILE_OPCODE:x}, \
        0x{fn3:x}, 0x{fn7:x}, {{rd}}, {{rs1}}, {{rs2}}"
    );
    let mut rd: u32;
    unsafe {
        ::core::arch::asm!(
            ".insn r 0x0B 0x{FN3} 0x{FN7}, {rd}, {rs1}, {rs2}",
            rd = out(reg) rd,
            rs1 = in(reg) rs1,
            rs2 = in(reg) rs2,
        );
    };

    return rd;
}
```

This doesn’t precisely match our implementation (see [`precompiles/macros/src/generation.rs`](https://github.com/nexus-xyz/nexus-zkvm/blob/main/precompiles/macros/src/generation.rs) for that), but this example code is conceptually the same.

`FN3` and `FN7` are dynamically generated on a per-guest-program-compilation basis. Concretely, guest programs are limited to using no more than 1,024 precompiles, and a procedural macro is responsible for generating an integer index in `[0, 1024)` for each precompile and embedding an (index → precompile) mapping into the compiled program’s code (again, see `generation.rs` for specifics).

When the VM loads a guest program, it first uses the mapping embedded in the binary to discover which precompiles it will need to load in order to offer the guest program its desired functionality. The static reserved opcode and the mapping embedded in the guest program binary together encode all the instruction information needed for the zkVM to dynamically execute and prove the precompiles needed by a guest program.

In order to verify a proof, the verifier also has to be able to discover the precompiles used by the guest program. The verifier does this almost exactly the same as the zkVM does. The only difference is that the verifier ignores the precompile’s implementation, only verifying that the precompile’s constraints are satisfied and the same as the ones proved by the zkVM. The Nexus ecosystem ensures that the verifier is able to seamlessly access the same precompile implementations as the zkVM.

### Executing Precompiles

Precompiles are distributed as shared libraries compiled for the host’s native platform (e.g., Linux or macOS). On startup, the zkVM is configured to search for and load precompiles from a set of directories and files. Each of these is loaded into memory using the platform-appropriate dynamic library loading mechanism (e.g., `dlopen` on Unixes).

Precompile implementations adhere to the precompile interfaces specified in `nexus-precompiles` (see [`precompiles/src/traits.rs`](https://github.com/nexus-xyz/nexus-zkvm/blob/main/precompiles/src/traits.rs)). These traits mirror the traits that define native instructions as closely as possible, the only difference being that precompiles use dynamic dispatch instead of static dispatch via generics. Again, Nexus-provided macros automate exporting these implementations under well-known names which the zkVM can discover.

It's worth noting that while dynamically loaded libraries typically use the C ABI, we chose to use the Rust ABI for the time being. Nexus’ macros and zkVM implementation hide this from precompile developers and users, but there is one user-visible consequence: precompiles must be built with the same Rust version as the zkVM. This is because Rust has no ABI stability guarantees, and after precompiles stabilize, we may switch internally to the completely stable C ABI.

Upon loading a guest binary, the zkVM searches for well-known Nexus precompile symbols in that binary. If it finds any, it will construct a table mapping precompile instructions, discussed above, to the precompile implementations found in the loaded libraries. The zkVM will then use this table to dynamically dispatch precompile calls to the appropriate implementation during emulation.

### Proving Precompiles

Because precompile instructions are conceptually no different from native instructions, the zkVM’s proving process is able to integrate them easily. We need no special treatment for precompiles in the tracing process; they are simply another kind of instruction present in the trace that the emulator generates for the prover.

The prover, however, does need to be aware of precompiles in the same way that the emulator is. The same table constructed during guest binary loading is used by the prover, but instead of fetching instruction implementations, it fetches constraint circuits. The prover then uses these circuits to constrain the trace of the precompile instructions in the same way that it does for native instructions.

## Precompile Development

Because of Nexus’ choice to prioritize the precompile consumer’s experience, developing precompiles is a more advanced task than developing guest programs. This section is intended to help precompile developers understand the steps required to develop a precompile, each either on the host or guest side.

Fortunately, there are still only a few steps in developing a precompile, and Nexus provides tooling to assist with each. To create a precompile, a developer must:

{% stepper %}
{% step %}

### Implement the precompile’s functionality (host)

Write the host-side implementation that performs the operation exposed by the precompile.
{% endstep %}

{% step %}

### Create the precompile’s circuit (host)

Design and provide the constraint circuit that the prover will use to constrain traces for the precompile instruction.
{% endstep %}

{% step %}

### Provide precompile consumers with an idiomatic interface (guest)

Package a small, idiomatic Rust guest-side interface (crate) so guest programs can use the precompile like a normal library.
{% endstep %}
{% endstepper %}

The host functionality and circuit will be packaged in the precompile’s shared library, while the guest interface is packaged in a Rust crate imported by guest programs. The host implementation needs no awareness of the guest interface, and the guest interface needs no awareness of how the precompile is implemented.

### Host-Side Development

The bulk of the work necessary to create a precompile is its host-side implementation and constraint circuit. Concretely, precompile developers must write a `struct` which implements `PrecompileInstruction` (which will be specified in [`precompiles/src/traits.rs`](https://github.com/nexus-xyz/nexus-zkvm/blob/main/precompiles/src/traits.rs) once full precompile support is published). The source code for this trait (and its supertraits) is extensively documented, and for the most up-to-date guide, refer to the rustdoc for `traits.rs` and the example precompiles in `precompiles/examples`.

### Guest-Side Development

The developer work involved with creating a guest-side interface for a precompile is minimal: the developer only needs to write a single function that provides idiomatic access to the precompile’s functionality. This function has to be defined by a macro for code-generation reasons (only upon compilation will the precompile’s opcode be calculated) but is otherwise a simple wrapper around the precompile’s raw instruction.

As a concrete example, consider a simple hash. The host-side implementation and macro might write a function like the following:

```rust
impl MyHashInstruction {
    pub fn emit_instruction(rs1: u32, rs2: u32) -> u32 {
        let mut rd: u32;

        unsafe {
            ::core::arch::asm!(
                "/* inst. details */ {rd}, {rs1}, {rs2}",
                rd = out(reg) rd,
                rs1 = in(reg) rs1,
                rs2 = in(reg) rs2,
            );
        }

        return rd;
    }
}
```

Where `emit_instruction` computes the hash of `rs2` bytes starting at memory address `rs1` and writes it to `rd`. We don’t want guest program developers to have any awareness of how the underlying instruction works, so the precompile developer would write a macro like the following:

```rust
// Package: my-precompile
// File: my_precompile/src/guest.rs

#[cfg(target_arch = "riscv32")]
#[macro_export]
macro_rules! generate_instruction_caller {
    ($path:path) => {
        pub trait HashCaller {
            fn hash(data: &[u8]) -> u32;
        }

        impl HashCaller for $path {
            fn hash(data: &[u8]) -> u32 {
                let data_ptr = data.as_ptr() as u32;
                let data_len = data.len() as u32;

                Self::emit_instruction(data_ptr, data_len)
            }
        }
    };
}
```

Then, when the guest program developer wants to use the precompile, their `use_precompiles!` macro will automatically call the `generate_instruction_caller!` macro, which will generate an ad-hoc `HashCaller` trait implementation for the precompile’s instruction. The guest program developer can then use the precompile like this:

```rust
use_precompiles!(my_precompile::MyHash);

#[nexus_rt::public_input(x)]
#[nexus_rt::main]
fn main(x: &[u8]) -> u32 {
    MyHash::hash(x)
}
```

Which is exactly the kind of experience we set out to provide for guest program developers.


# Benchmarking

```bash
# Arrays for input and output
public_inputs=(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)
public_outputs=(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)

# Loop through the arrays
for i in "${!public_inputs[@]}"; do
    public_input="${public_inputs[$i]}"
    public_output="${public_outputs[$i]}"

    echo "Running with PUBLIC_INPUT=$public_input and PUBLIC_OUTPUT=$public_output"

    # Run the Rust program with environment variables set
    # Ensure the Rust program is built in release mode
    PUBLIC_INPUT=$public_input PUBLIC_OUTPUT=$public_output cargo run --release

    echo "----------------------------------------"
done
```

Results (units in milliseconds)

| n-th Fibonacci | Compile (ms) | Prove (ms) | Verify (ms) | Total Time (ms) | Proof size (bytes) |
| -------------- | -----------: | ---------: | ----------: | --------------: | -----------------: |
| 0              |          161 |       1592 |          11 |            1764 |              51968 |
| 1              |          149 |       1522 |          11 |            1684 |              51968 |
| 2              |          148 |       1514 |          11 |            1675 |              51968 |
| 3              |          151 |       1538 |          11 |            1701 |              51440 |
| 5              |          151 |       1477 |          12 |            1641 |              51968 |
| 8              |          128 |       1465 |          11 |            1606 |              46008 |
| 13             |          136 |       1454 |          11 |            1602 |              49304 |
| 21             |          138 |       1440 |          12 |            1591 |              49880 |
| 34             |          149 |       1551 |          12 |            1713 |              51968 |
| 55             |          149 |       1422 |          13 |            1584 |              51440 |
| 89             |          151 |       1432 |          12 |            1596 |              51440 |
| 144            |          153 |       1427 |          11 |            1593 |              49460 |
| 233            |          149 |       1434 |          12 |            1596 |              50384 |
| 377            |          138 |       1417 |          11 |            1567 |              50368 |
| 610            |          148 |       1453 |          11 |            1613 |              51968 |
| 987            |          150 |       1510 |          12 |            1673 |              49280 |
| 1597           |          149 |       1443 |          11 |            1604 |              50384 |
| 2584           |          146 |       1460 |          13 |            1620 |              50864 |
| 4181           |          147 |       1449 |          11 |            1610 |              51968 |
| 6765           |          149 |       1440 |          11 |            1602 |              51968 |

Observation: Proving time is roughly constant for the first 20 Fibonacci numbers — likely because zkVM setup overhead dominates while the actual Fibonacci computation is still small.

Larger inputs (around the 200th Fibonacci)

| n-th Fibonacci | Compile (ms) | Prove (ms) | Verify (ms) | Total Time (ms) | Proof size (bytes) |
| -------------- | -----------: | ---------: | ----------: | --------------: | -----------------: |
| 200            |          156 |      17416 |         189 |           17762 |              58544 |
| 201            |          154 |      17179 |         183 |           17518 |              59904 |
| 202            |          152 |      17468 |         183 |           17804 |              59248 |
| 204            |          158 |      17238 |         181 |           17579 |              59248 |
| 205            |          151 |      17242 |         184 |           17579 |              57920 |

Observation: Proving time increases substantially with input size, while verification time remains small compared to proving time.

{% stepper %}
{% step %}

### Optimize Guest Programs

Focus on minimizing RISC-V cycles in guest programs. The benchmark shows the number of RISC-V cycles directly impacts proving time, which is the dominant component of total execution time.
{% endstep %}

{% step %}

### Consider Algorithmic Efficiency

For computationally demanding scenarios, prioritize efficient algorithms and implementations. The Fibonacci example demonstrates how complexity can rapidly increase proving time.
{% endstep %}

{% step %}

### Profile Regularly

Regularly profile Nexus zkVM projects to identify performance bottlenecks and optimization opportunities.
{% endstep %}
{% endstepper %}

{% hint style="info" %}
Note: Verification time remains negligible in these benchmarks compared to proving time; optimizing the proving workload yields the largest gains.
{% endhint %}

Related links:

* <https://docs.nexus.xyz/zkvm/proving/precompiles>
* <https://docs.nexus.xyz/zkvm/walkthroughs/galeshapley>


# Use Case: Stable Matching

Every year in the United States, \~50,000 graduating medical students (doctors, for short) are placed into the residencies where they will begin their careers by the [National Resident Matching Program](https://www.nrmp.org/) (or The Match). To do so, the program finds a stable matching: an assignment of doctors to medical programs (hospitals, for short) such that there is no pair of doctor and hospital that would prefer to be assigned each other over the match(es) they ended up with. Each candidate doctor ranks their preferred hospitals and each hospital ranks their preferences in candidates, and the matching proceeds algorithmically.

One of the original problems in the field of algorithmic mechanism design, the matching program uses the [Roth-Peranson algorithm (1999)](https://www.aeaweb.org/articles?id=10.1257/aer.89.4.748), a modification of the earlier [Gale-Shapley algorithm (1962)](https://www.tandfonline.com/doi/pdf/10.1080/00029890.1962.11989827). These contributions in large part earned Roth & Shapley the 2012 Nobel Prize in Economics.

The Match is a classic example of a computation [whose transparency and accountability can benefit from private verifiable computation](https://scholarship.law.upenn.edu/penn_law_review/vol165/iss3/3/): at present, doctors receive no direct assurance that an algorithmic decision that will shape their careers (and their career earnings) is being executed correctly. However, The Match cannot simply publish a transcript of the matching, as it would harm the privacy of the doctors in particular (“Alice said she wanted to come back home for residency, but she didn’t rank any local hospitals” or “when we interviewed Bob he said fifteen years ago he ranked us highly in The Match, but he only had us seventh”). [Anonymizing the doctors can only accomplish so much](https://heinonline.org/HOL/LandingPage?handle=hein.journals/uclalr57\&div=48\&id=\&page=) as it offers only the illusion of privacy; modern re-identification techniques routinely pierce de-identified data. As a result, anonymization *fails to actually anonymize*. Our goal is to create a system that provides complete transparency about the correctness of the matching algorithm while preserving the privacy of all preference rankings.

Specifically, we want to generate a cryptographic proof that demonstrates the Gale-Shapley algorithm was executed correctly and produced a stable matching, without revealing any individual’s private preferences. The Nexus zkVM enables us to achieve exactly this balance between accountability and privacy.

***

{% stepper %}
{% step %}

### Implementation — initialize host and guest programs

Follow the [SDK Quick Start](/zkvm/development/sdk-quick-start). After installing the zkVM, to initialize the host and guest programs, run:

{% code title="Initialize host and guest" %}

```
$ cargo nexus host matching
```

{% endcode %}
{% endstep %}

{% step %}

### Guest Program

This guest program is the program to be proven; it implements the matching itself using the Gale-Shapley approach.

matching/src/guest/src/main.rs

{% code title="stable\_matching implementation" %}

```rust
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]

extern crate alloc;
use alloc::collections::BTreeSet;
use alloc::{vec, vec::Vec};

fn stable_matching(
    n: usize,
    mut employers: BTreeSet<usize>,
    mut candidates: BTreeSet<usize>,
    employer_prefs: Vec<Vec<usize>>,
    candidate_prefs: Vec<Vec<usize>>,
) -> Vec<Option<usize>> {
    let mut hires: Vec<Option<usize>> = vec![None; n];
    while !employers.is_empty() {
        let next = employers.pop_first().unwrap();
        let prefs = &employer_prefs[next];

        for c in prefs {
            if candidates.contains(c) {
                hires[next] = Some(*c);
                assert!(candidates.remove(c));
                break;
            } else {
                let current = hires
                    .iter()
                    .position(|&x| x.is_some() && *c == x.unwrap())
                    .unwrap();
                if candidate_prefs[*c].iter().position(|&x| next == x)
                    < candidate_prefs[*c].iter().position(|&x| current == x)
                {
                    assert!(employers.insert(current));
                    hires[next] = Some(*c);
                    hires[current] = None;
                    break;
                }
            }
        }
    }

    hires
}
```

{% endcode %}

The function takes:

* a set of candidates (doctors),
* a set of employers (hospitals),
* preference rankings in matrix form (row i of `employer_prefs` encodes the ranking for the i-th employer),
* a parameter `n = employers.len()`.

It returns a vector `hires` of length `n`, where `hires[j] = i` means the j-th hospital hires the i-th doctor.

You also need a `main` to handle zkVM inputs/outputs and declare which inputs are public/private.

matching/src/guest/src/main.rs

{% code title="guest main" %}

```rust
#[nexus_rt::main]
#[nexus_rt::private_input(prefs)]
#[nexus_rt::public_input(lists)]
fn main(
    prefs: (Vec<Vec<usize>>, Vec<Vec<usize>>),
    lists: (BTreeSet<usize>, BTreeSet<usize>),
) -> Vec<Option<usize>> {
    let (mut employer_prefs, mut candidate_prefs) = prefs;
    let (mut employers, mut candidates) = lists;

    let n = employers.len();
    let m = candidates.len();

    nexus_rt::print!("Matching {} employers with {} candidates... ", n, m);

    let hires = stable_matching(n, employers, candidates, employer_prefs, candidate_prefs);

    nexus_rt::println!("completed.");

    hires
}
```

{% endcode %}

Note: inputs default to private unless explicitly declared public. The public inputs/outputs here leak only which hospital each candidate matched with (the `hires` vector), which is acceptable for auditing the algorithm while keeping preferences private.
{% endstep %}

{% step %}

### Host Program

The host program compiles the guest and runs it with the provided preferences (these preferences are private inside the host).

matching/src/main.rs

{% code title="host main" %}

```rust
use nexus_sdk::{
    compile::{cargo::CargoPackager, Compile, Compiler},
    stwo::seq::Stwo,
    ByGuestCompilation, Local, Prover, Viewable,
};
use std::collections::BTreeSet;

type Matches = Vec<Option<usize>>;
type Prefs = (Vec<Vec<usize>>, Vec<Vec<usize>>);
type Lists = (BTreeSet<usize>, BTreeSet<usize>);

const PACKAGE: &str = "guest";

fn main() {
    println!("Compiling guest program...");
    let mut prover_compiler = Compiler::<CargoPackager>::new(PACKAGE);
    let prover: Stwo<Local> =
        Stwo::compile(&mut prover_compiler).expect("failed to compile guest program");

    let n = 10;
    let m = 8;
    assert!(m < n);

    let mut employers = BTreeSet::<usize>::new();
    let mut candidates = BTreeSet::<usize>::new();

    for i in 0..n {
        employers.insert(i);
        if i < m {
            candidates.insert(i);
        }
    }

    let employer_prefs = vec![\
        vec![4, 1, 2, 7, 3, 0, 6, 5],\
        vec![0, 1, 4, 5, 7, 2, 6, 3],\
        vec![3, 7, 4, 6, 2, 0, 5, 1],\
        vec![4, 6, 0, 2, 7, 3, 5, 1],\
        vec![2, 6, 1, 3, 7, 0, 4, 5],\
        vec![1, 4, 5, 3, 7, 0, 6, 2],\
        vec![1, 5, 4, 0, 6, 2, 7, 3],\
        vec![3, 6, 1, 2, 7, 0, 5, 4],\
        vec![7, 5, 3, 4, 1, 6, 2, 0],\
        vec![0, 6, 1, 4, 5, 2, 7, 3],\
    ];

    let candidate_prefs = vec![\
        vec![0, 5, 7, 8, 3, 1, 4, 2, 6, 9],\
        vec![8, 2, 0, 9, 3, 4, 5, 6, 1, 7],\
        vec![3, 6, 4, 0, 5, 9, 7, 8, 2, 1],\
        vec![2, 9, 7, 3, 4, 1, 5, 8, 0, 6],\
        vec![7, 5, 9, 4, 6, 8, 0, 1, 3, 2],\
        vec![4, 1, 7, 6, 2, 9, 8, 0, 5, 3],\
        vec![0, 5, 3, 6, 8, 7, 4, 1, 9, 2],\
        vec![7, 5, 6, 1, 4, 0, 2, 8, 9, 3],\
    ];

    println!("Proving execution of vm...");
    let (view, proof) = prover.prove_with_input::<Prefs, Lists>(
        &(employer_prefs, candidate_prefs),
        &(employers, candidates),
    ).expect("failed to prove program");

    println!(
        ">>>>> Logging\n{}<<<<<",
        view.logs().expect("failed to retrieve debug logs").join("")
    );
    assert_eq!(view.exit_code().expect("failed to retrieve exit code"), nexus_sdk::KnownExitCodes::ExitSuccess as u32);

    let hires = view.public_output::<Matches>().expect("failed to retrieve public output");

    print!("Found matching (employer, candidate): ");
    for i in 0..n {
        if i > 0 {
            print!(", ")
        }
        print!(
            "({}, {})",
            i,
            if hires[i].is_some() {
                hires[i].unwrap().to_string()
            } else {
                "None".to_string()
            }
        );
    }
    println!("\n");

    /// the host program would go on to publish `view` and `proof` publicly.
}
```

{% endcode %}

The ranking matrices shown above are random example data for demonstration.
{% endstep %}

{% step %}

### Verification

Once the program, its public input/output (`view`) and the `proof` are published, any interested party can verify correctness without access to preferences.

{% code title="verifier snippet" %}

```rust
println!("Verifier recompiling guest program...");
let mut verifier_compiler = Compiler::<CargoPackager>::new(PACKAGE);
let path = verifier_compiler.build().expect("failed to (re)compile guest program");

print!("Verifying execution...");
proof.verify_expected_from_program_path::<&str, Lists, Matches>(
  &(employers, candidates),                       // at view.public_input
  nexus_sdk::KnownExitCodes::ExitSuccess as u32,  // at view.exit_code
  &hires,                                         // at view.public_output
  &path,                                          // path to program binary
  &[]                                             // no associated data,
).expect("failed to verify proof");
```

{% endcode %}

If the published matching (`hires`) has been tampered with in a way that violates Gale-Shapley guarantees, verification will fail.

Limitation: a verifier cannot directly confirm that the private preference matrices themselves are the ones originally provided by participants. The zkVM guarantees there exists some private input for which the execution is correct, not that the private input specifically matches an off-chain record. To mitigate this, participants can submit salted hashes of their preferences (with per-participant passphrases) that the guest program includes (hashed) in public output. Each participant can then verify their own preference hash matches what was used in the execution without revealing preferences to others.
{% endstep %}

{% step %}

### Conclusion

This walkthrough demonstrates how the Nexus zkVM can provide verifiable transparency for algorithmic decision-making while preserving privacy. Achievements:

* A publicly auditable guest program implementing stable matching.
* Proof generation that keeps preference rankings private.
* A verification mechanism for any party to confirm correct execution.
* A framework balancing individual privacy with institutional accountability.

The approach transforms the medical residency matching process from a “trust us” system to a “verify for yourself” system and can be applied to other algorithmic decision systems (financial algorithms, ML-based hiring/lending/healthcare) where transparency and privacy must coexist.
{% endstep %}
{% endstepper %}


# Use Case: Program Execution

### Introduction

*As this walkthrough describes a more advanced use case for the zkVM, we recommend reviewing both the* [SDK Quick Start](/zkvm/development/sdk-quick-start) *and the simpler* [Stable Matching](/zkvm/walkthroughs/use-case-stable-matching) *walkthrough first.*

By definition, a zkVM is designed to prove the correctness of a program’s execution. However, a more advanced use case arises when the **program to be proven** is not the code running directly in the zkVM, but is instead provided **as input** to the zkVM.

In this case, the zkVM executes an **interpreter** as its guest program, which then reads, interprets, and runs the input program inside the zkVM. This effectively allows the zkVM to generate a proof for the execution of arbitrary, dynamically supplied programs.

Consider the following guest program in which `program_to_be_proven` is nested inside as input to the zkVM:

```rust
fn digest(program: &ElfFile) -> Vec<u8> { ... }

fn rv32i_interpret(program: &ElfFile) -> Vec<u8> { ... }

#[nexus_rt::main]
#[nexus_rt::private_input(program_to_be_proven)]
#[nexus_rt::public_input(program_digest)]
fn main(program_to_be_proven: ElfFile, program_digest: Vec<u8>) -> Vec<u8> {
    assert_eq!(digest(&program_to_be_proven), &program_digest);

    nexus_rt::print!("Program digest {} validated, executing program... ", program_digest);

    let output = rv32i_interpret(&program_to_be_proven);

    nexus_rt::println!("completed.");

    output
}
```

At first glance, this pattern may seem redundant—why not just run `program_to_be_proven` directly as the guest program? However, this level of **indirection** unlocks powerful capabilities that significantly expand the utility of the zkVM.

#### Enabling Private Program Execution

The first benefit, illustrated by the example above, is the ability to treat the **program to be proven** as a **private input**. This allows the zkVM to prove the execution of programs **without revealing their source**, making it possible to build **private smart contracts** on-chain, among other use cases.

By comparing the program’s digest to a **public input**, verifiers can still check meaningful properties such as:

* Consistency of the private program across multiple invocations.
* Conformance to a known, audited version without exposing implementation details.

This digest check enables **trust minimization** while preserving **privacy**.

#### Generalization Across Programs with a Single zkVM Binary

A second, more technical benefit is that this structure enables the zkVM to handle a wide range of programs through a **single, fixed guest binary** — the interpreter.

With careful planning around memory constraints (e.g. stack, heap, and output size bounds), this pattern allows:

* The proving key and public parameters can be shared and reused across many programs, which simplifies definitions for [non-transparent provers which may require per-program setup](https://blog.nexus.xyz/the-murky-proof-system-waters-part-ii/).
* Deployment pipelines for applications and smart contracts verifying zkVM proofs on-chain can be simplified significantly.
* Systems using non-transparent proving systems can be managed more easily, even though they often require per-program setup (note: the `Stwo` prover used here is transparent).

This approach decouples the proving circuit from individual guest programs, significantly improving **composability** and **maintainability**.

### The Lambda Calculus Model

Instead of working through this design at the full complexity of the RISC-V32i instruction set, this walkthrough uses a simpler computational model: the [lambda calculus](https://en.wikipedia.org/wiki/Lambda_calculus). The example demonstrates the core concept with a minimal interpreter implemented as a guest program inside the zkVM.

***

### Implementation

To start, follow the [SDK Quick Start](/zkvm/development/sdk-quick-start). After installing the zkVM, initialize the host and guest programs:

```bash
$ cargo nexus host lambda_calculus
```

Begin by creating a shared library for the guest and host program, located within the guest program crate.

#### Shared Library

The first part of the library defines terms in the calculus:

lambda\_calculus/src/guest/src/common.rs

```rust
#![cfg_attr(no_std)]

extern crate alloc;

use alloc::boxed::Box;
use core::fmt::{Debug, Display};
use serde::{Serialize, Deserialize};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[repr(transparent)]
pub struct DeBruijnIndex(usize);

impl From<usize> for DeBruijnIndex {
    fn from(value: usize) -> Self {
        Self(value)
    }
}

impl Display for DeBruijnIndex {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
enum Term<R> {
    Var(DeBruijnIndex),
    Lambda(R),
    Apply(R, R),
}

impl<R: Display> Display for Term<R> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Term::Var(v) => write!(f, "{v}"),
            Term::Lambda(t) => {
                write!(f, "(\\")?;
                write!(f, "{t}")?;
                write!(f, ")")
            }
            Term::Apply(t1, t2) => {
                write!(f, "(")?;
                write!(f, "{t1}")?;
                write!(f, " ")?;
                write!(f, "{t2}")?;
                write!(f, ")")
            }
        }
    }
}
```

This defines an enum `Term` for possible term kinds: indexed `Var`, `Lambda`, and `Apply`, and includes trait implementations for construction and debugging. Next, full expressions and evaluation routines:

lambda\_calculus/src/guest/src/common.rs

```rust
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Expr(Term<Box<Self>>);

impl Display for Expr {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl PartialEq for Expr {
    fn eq(&self, other: &Self) -> bool {
        self.clone().eval().structural_eq(&other.clone().eval())
    }
}

impl Eq for Expr {}

impl Expr {
    pub fn var<T: Into<DeBruijnIndex>>(binding: T) -> Self {
        Self(Term::Var(binding.into()))
    }

    pub fn lambda<T: Into<Box<Self>>>(inner: T) -> Self {
        Self(Term::Lambda(inner.into()))
    }

    pub fn apply<T1, T2>(left: T1, right: T2) -> Self
    where
        T1: Into<Box<Self>>,
        T2: Into<Box<Self>>,
    {
        Self(Term::Apply(left.into(), right.into()))
    }

    pub fn identity() -> Self {
        Self::lambda(Self::var(0))
    }

    pub fn omega() -> Self {
        let expr = Self::lambda(Self::apply(Self::var(0), Self::var(0)));
        Self::apply(expr.clone(), expr)
    }

    pub fn step(&self) -> Self {
        fn subst(body: Expr, arg: Expr, depth: usize) -> Expr {
            match body.0 {
                Term::Var(i) => {
                    if i.0 == depth {
                        arg
                    } else {
                        Expr::var(i)
                    }
                }
                Term::Lambda(e) => subst(*e, arg, depth + 1),
                Term::Apply(e1, e2) => {
                    Expr::apply(subst(*e1, arg.clone(), depth), subst(*e2, arg, depth))
                }
            }
        }

        // attempt beta reduction
        if let Term::Apply(e1, e2) = &self.0 {
            if let Term::Lambda(e) = &e1.0 {
                return subst(*e.clone(), *e2.clone(), 0);
            }
        }

        self.clone()
    }

    pub fn structural_eq(&self, other: &Self) -> bool {
        match (&self.0, &other.0) {
            (Term::Var(i), Term::Var(j)) => *i == *j,
            (Term::Lambda(e), Term::Lambda(f)) => e.structural_eq(f),
            (Term::Apply(e1, e2), Term::Apply(f1, f2)) => {
                e1.structural_eq(f1) && e2.structural_eq(f2)
            }
            _ => false,
        }
    }

    pub fn eval(self) -> Self {
        let mut curr = self;
        loop {
            let next = curr.step();
            if next.structural_eq(&curr) {
                return curr.clone();
            } else {
                curr = next;
            }
        }
    }
}
```

This provides `step` and `eval` routines to reduce expressions into stable forms.

To make the common library work across both host and guest:

* Update the guest crate to depend on `serde` (used for serialization/deserialization of `Expr`):

lambda\_calculus/src/guest/Cargo.toml

```toml
...

[dependencies]
nexus-rt = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "0.3.0", version = "0.3.0" }
postcard = { version = "1.1.1", default-features = false, features = ["alloc"] }
serde = { version = "1.0", default-features = false, features = ["derive"] }

...
```

* Export the common code as a Rust library:

lambda\_calculus/src/guest/src/lib.rs

```rust
#![no_std]

pub mod common;
pub use common::*;
```

* Make the library visible to the host program:

lambda\_calculus/Cargo.toml

```toml
...

[dependencies]
nexus-sdk = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "0.3.0", version = "0.3.0" }
guest = { path = "./src/guest" }

...
```

#### Guest Program

As an `Expr` is a program in the lambda calculus, implement a simple interpreter (here omitting hash-based checking):

lambda\_calculus/src/guest/src/main.rs

```rust
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]

use guest::Expr;

#[nexus_rt::main]
#[nexus_rt::private_input(program_to_be_proven)]
fn main(program_to_be_proven: Expr) -> Expr {
    program_to_be_proven.eval()
}
```

#### Host Program

Create a host program that compiles the guest, provides a private program as input, and produces a proof of its execution:

lambda\_calculus/src/main.rs

```rust
use nexus_sdk::{
    compile::{cargo::CargoPackager, Compile, Compiler},
    stwo::seq::Stwo,
    ByGuestCompilation, Local, Prover, Viewable,
};

use guest::Expr;

const PACKAGE: &str = "guest";

fn main() {
    println!("Compiling guest program...");
    let mut prover_compiler = Compiler::<CargoPackager>::new(PACKAGE);
    let prover: Stwo<Local> =
        Stwo::compile(&mut prover_compiler).expect("failed to compile guest program");

    let program_to_be_proven = Expr::apply(Expr::identity(), Expr::var(42));

    println!("Proving execution of vm...");
    let (view, proof) = prover.prove_with_input::<Expr, ()>(
        &program_to_be_proven,
        (),
    ).expect("failed to prove program");

    assert_eq!(view.exit_code().expect("failed to retrieve exit code"), nexus_sdk::KnownExitCodes::ExitSuccess as u32);

    let output = view.public_output::<Expr>().expect("failed to retrieve public output");

    println!("Reduced expression: {}", output);
}
```

### Verification

Verification does not require access to the private program. A verifier can recompile the guest program and verify the proof against the expected outputs:

```rust
println!("Verifier recompiling guest program...");
let mut verifier_compiler = Compiler::<CargoPackager>::new(PACKAGE);
let path = verifier_compiler.build().expect("failed to (re)compile guest program");

print!("Verifying execution...");
proof.verify_expected_from_program_path::<&str, (), Expr>(
  &(),                                           // at view.public_input
  nexus_sdk::KnownExitCodes::ExitSuccess as u32, // at view.exit_code
  &output,                                       // at view.public_output
  &path,                                         // path to program binary
  &[]                                            // no associated data,
).expect("failed to verify proof");
```

This demonstrates using the zkVM to prove the correctness of executing a private program.

### Conclusion

This walkthrough demonstrates how the Nexus zkVM can be applied to fundamental computational models, illustrating versatility beyond practical applications. The implementation proves correct execution of lambda calculus programs while keeping program logic private.

Key achievements:

1. A shared library architecture enabling code reuse between guest and host programs.
2. A clean abstraction for lambda calculus expressions with evaluation semantics.
3. A zkVM-based interpreter that generates proofs for program execution.
4. A verification system that confirms correctness without exposing the original program.

This example shows that zkVMs can handle abstract computational models, not just concrete algorithms, and that privacy-preserving verification principles generalize across domains. The approach transforms program execution from “trust the interpreter” to “verify the computation,” providing mathematical assurance that programs are evaluated correctly according to their formal semantics.

Links for further reading:

* Use Case: Stable Matching: <https://docs.nexus.xyz/zkvm/walkthroughs/galeshapley>
* The Complete Specification: <https://docs.nexus.xyz/zkvm/specifications/zkvm-overview>


# The Nexus Whitepaper

**Nexus 1.0: Enabling Verifiable Computation**

The Nexus 1.0 whitepaper introduces the Nexus zkVM — a zero-knowledge virtual machine capable of proving any computation. It covers the full system design: the Nexus Virtual Machine (NVM), co-processors, recursive proof systems, and the Nexus Network's distributed prover architecture.

Key topics:

* **Nexus zkVM** — a universal proving machine for any stateful ISA (RISC-V, EVM, Wasm), architected for massively parallelised incremental proof generation
* **Nexus Virtual Machine (NVM)** — a minimal, extensible virtual CPU designed to maximise prover performance
* **Proof systems** — HyperNova, Parallel Nova, Parallel HyperNova; folding/accumulation schemes enabling IVC and PCD
* **Nexus Network** — a large-scale distributed prover network aggregating heterogeneous compute to prove 1B+ CPU cycles

{% file src="/files/J7FJzPwnFdmuZuyCOOE3" %}


# The Complete Specification

The Nexus zkVM 3.0 Specification provides an overview of the runtime and machine architecture of the zkVM, as well as a full, formal description of the constraints and proving integration.

In this Specifications section, we provide accessible introductions to some of the core concepts from the specification.

<table data-view="cards"><thead><tr><th>Title</th><th data-card-target data-type="content-ref">Target</th></tr></thead><tbody><tr><td>Home</td><td><a href="https://docs.nexus.xyz/home">https://docs.nexus.xyz/home</a></td></tr><tr><td>Nexus Layer 1 — Overview</td><td><a href="https://docs.nexus.xyz/layer-1/vision/overview">https://docs.nexus.xyz/layer-1/vision/overview</a></td></tr><tr><td>Nexus zkVM</td><td><a href="/pages/2fee3c5b002f76ad0b4f3fc5b1247c8207385ff6">/pages/2fee3c5b002f76ad0b4f3fc5b1247c8207385ff6</a></td></tr><tr><td>Web3 Fundamentals — Overview</td><td><a href="https://docs.nexus.xyz/essentials/web3-fundamentals/overview">https://docs.nexus.xyz/essentials/web3-fundamentals/overview</a></td></tr><tr><td>Use Case: Program Execution</td><td><a href="/pages/7d968cf98335e73d5f50320113f3836829feb400">/pages/7d968cf98335e73d5f50320113f3836829feb400</a></td></tr><tr><td>Machine Architecture</td><td><a href="/pages/d5430652a02631dc3db48dded6b2fb0695a0618f">/pages/d5430652a02631dc3db48dded6b2fb0695a0618f</a></td></tr><tr><td>Full zkVM Specification: zkVM Overview</td><td><a href="#content-area">#content-area</a></td></tr></tbody></table>


# Machine Architecture

The primary use of the Nexus zkVM machine architecture is to support the verifiable computation of the full zkVM. However, it is a well-defined virtual machine in its own right, and some of its key components are described below.

### Architectural overview

The machine architecture is built around an instruction set, not the same as, but close enough to the RISC-V RV32I instruction set. The primary difference is the lack of a few supported instructions, such as `fence` and `ebreak`. The machine uses a modified Harvard architecture in which the program, data, and input-output memory segments are distinct and permissioned with respect to whether they can be read from, written to, or both.

### Execution Model

The Nexus zkVM is designed around a “only prove what you use” memory architecture, where unused memory does not need to be proven. As a trade-off, the zkVM requires that the amounts of memory used by most of the program segments (all but the heap) must be known before execution — even though the sizes of the stack and output are often execution-dependent.

To avoid this chicken-and-egg problem, the machine architecture operates on a two-pass tracing model: the program is first executed in a (mostly) traditional Harvard architecture, and statistics are kept as to the resultant memory usage. The guest program is then executed again using the same inputs in a modified Harvard architecture with a fixed-memory organization determined from the statistics of the first execution, which is more conducive to proving.

### Execution Environment

Through environment calls the guest program can interact with the execution environment. The machine architecture supports six environment calls. Two of these calls are used for debugging (logging) and optimization (cycle counting), and these are no-ops during the second, proven tracing. The other four are used for setting up stack and heap pointers in the second, fixed memory model, as well as for reading off the private input tape and exiting the guest program, analogous to the exit system calls (like Rust’s `std::process::exit(code: i32) -> !}` provided by most programming languages).

### Memory Layout

The machine architecture uses distinct component memories. Each memory has three attributes: in which address space it exists, with what permission structure (read-write, read-only, write-only, or no access), and whether it is of fixed or variable size.

For the first pass, the organization of the memory is a mostly traditional Harvard architecture, with five distinct address spaces:

* (i) the cpu registers,
* (ii) the public input,
* (iii) the error code and public output,
* (iv) the associated data, and
* (v) the RAM containing both the program and data segments (including the stack and heap).

Other than the joining of the program and data memories this forms a relatively traditional Harvard architecture. In terms of permissions and sizes:

* (i) is read-write and fixed,
* (ii) is read-only and fixed,
* (iii) is write-only and variable,
* (iv) is no-access and fixed, and
* (v) is variable and mixed read-only (for static global variables) and read-write (for the remaining global variables and the entire data memory).

For (v) the guest program itself has no access to its instructions: they can only be accessed by the CPU.

For the second tracing pass, the memories are combined into a fixed-size, linear memory layout with one single, unified address space. As this is the memory layout used in tracing, the zkVM is best understood as a modified Harvard architecture, as the permission structures on the segments are maintained but they no longer exist in distinct memories. Also, a small additional read-only 8-byte segment containing well-known location pointers is introduced to enable the runtime to successfully access the now relocated public input and output segments.

### Registers

The zkVM machine has 32 registers that hold 32-bit word values. When tracing the program the zkVM stores the state of the registers separate from its record of memory operations. However, it also reserves the addresses `0x00-0x7F` so that prover integrations are able to identify the registers with those addresses should the prover need to consider the entire state of the machine to lie within a single address space.

### Well-Known Location Pointers

When tracing, the machine architecture also reserves two words of memory, the first from `0x80-0x83` and the second from `0x84-0x87`, which contain pointers to other memory locations for use by the runtime to manage input and output handling. These pointers existing in a well-known location for the runtime to access enables the zkVM to dynamically situate the input and output memory segments within the unified, linear architecture while still allowing the guest program easy access to their contents.

### Program Memory

The zkVM executes a guest program encoded in an ELF binary. The core of such a binary is the program consisting of a sequence of instructions and supporting read-only data (such as that contained in the `.rodata` segment). Before the zkVM executes the guest, this data must be loaded into a read-only segment of program memory, and after that remains unchanged during execution.

Additionally, the binary may contain read-write segments, such as the `.bss` and `.data` segments commonly used by programming languages to store global variables. Being writable, these segments must also be private over the course of the execution post-initialization. As such, the machine functionally treats these segments as a small part of the RAM that is non-contiguous with it, but with additional constraints to guarantee they are initialized as specified in the binary.

To simplify management of the dual-nature of these segments as both part of the program but also writable, the zkVM keeps the program memory and data memory in the same address space during the first-pass tracing, but with distinct permissions for the writable vs. read-only components. Those permissions are maintained in the unified address space of the linear memory model used in the second pass.

### Public Input

The public input segment contains an input of length `n` bytes prefaced by a four-byte (one 32-bit word) segment `n` so that the guest program can determine the length of the input made available to it. To read from the input the runtime invokes a custom instruction `rin`. During the second tracing pass — which is the one proven — `rin` is treated as a pseudoinstruction equivalent to `lb`, and the offset is loaded from the first word of the well-known segment (addresses `0x80-0x83` in the unified address space of the linear memory model). During both tracing passes, the contents of the segment are read-only — the zkVM will halt if an attempt is made by the guest program to write to those memory segments.

### Associated Data

For many zkVM use cases it can be useful to be able to bind arbitrary contextual information about an execution or the context of the proving into the proof itself. For example, from the perspective of the zkVM the program is just a compiled binary. By incorporating the hash of the program as originally written in a high-level language — such as Rust or Python — the proof can carry a reference to that code for use by the verifier, such as for auditing its functional correctness or the correctness of its compilation. In the particular case where the program forms a standalone software package, such as a Cargo crate or Python wheel, then binding in the hash of that package can even relate the proof to the broader software ecosystem.

To support binding arbitrary external information into the proof, the machine architecture contains an associated data memory segment that the prover can populate with an arbitrary bytestring. This segment is no-access within the Harvard architecture — it can neither be written to nor read from during an execution. But, the verifier otherwise treats it as a public input segment with checked contents that can then be used post-verification for application-focused infrastructure built on top of the proof, such as the aforementioned auditing. The associated data is placed before the public output and exit code, so that the memory regions after it form a contiguous space of writable segments.

### Public Output

The public output and exit code work in much the same way as the public input, except the relevant custom instruction is `wou` — interpreted on the second pass as `sb` — and the offset is loaded from the second word of the well-known segment (addresses `0x84-0x87` in the unified address space of the linear memory model). Otherwise, the most significant difference is that the single-word exit code segment and the public output segment are write-only, rather than read-only.

During the first tracing pass the public output segment can grow arbitrarily (up to addressing limits) to support additional written output. The length of the resultant output is then reserved ahead of time for the memory segment for the second and proven tracing pass.

### Data Memory

The zkVM includes a RAM. Instructions can read from the data memory, write to the data memory, or leave the data memory untouched. The primary use of the data memory is to store the stack and the heap.

During the first tracing pass, its size is variable and the stack and heap grow towards each other, as is standard. During the second tracing pass the stack and heap still grow towards each other, but are given fixed-size segments within which to do so. As a consequence, the size of the data memory is limited to only what is needed, enabling quicker proving and smaller proofs in the zkVM as only memory that is used is “paid for” by needing to prove its contents.


# Proving Instructions

The final component of the Nexus zkVM is the execution component, which is responsible for enforcing the correct execution of the instructions supported by the VM. It is designed with modularity and extensibility in mind.

Currently, this component provides support for the Nexus Virtual Machine (NVM) instruction set described in Section 2 of [the specification](/zkvm/specifications/the-complete-specification). The NVM instruction set is closely related to the RISC-V RV32I unprivileged instruction set found in [the RISC-V specification](https://drive.google.com/file/d/1s0lZxUZaa7eV_O0_WsZzaurFLLww7ou5/view). As a result, existing tooling for RISC-V RV32I can usually be used without modification.

We discuss some of the constraints for this component when we describe our [example](/zkvm/specifications/proving-an-example), but details of all the constraints for this component can be found in Section 8 of [the specification](/zkvm/specifications/the-complete-specification).


# Proving the CPU

The CPU component of the Nexus zkVM’s constraint system is responsible for ensuring that each state transition is correct. In particular, it is responsible for ensuring that the [instruction fetch](https://en.wikipedia.org/wiki/Classic_RISC_pipeline#Instruction_fetch), [instruction decode](https://en.wikipedia.org/wiki/Classic_RISC_pipeline#Instruction_decode), and [write-back](https://en.wikipedia.org/wiki/Classic_RISC_pipeline#Writeback) stages of the classic RISC pipeline are correct. It also facilitates connecting other stages of the pipeline with their respective constraint circuits.

Concretely, the CPU component performs the following tasks for each CPU cycle:

* Interacts with the program memory to fetch the next instruction pointed to by the program counter.
* Decodes that instruction and verifies that it is well-formed.
* Interacts with the register memory component to read the values associated with the instruction’s operands.
* Interacts with the execution component to execute the just-fetched instruction.
* Interacts with the register memory to perform write-back.

We discuss some of the constraints for this component when we describe our [example](/zkvm/specifications/proving-an-example), but details of all the constraints for this component can be found in Section 4 of [the specification](/zkvm/specifications/the-complete-specification).


# Proving Memory

The Nexus zkVM has three different components for handling the behavior of each type of memory used by the zkVM. Those types are:

* Program memory: a byte-addressed read-only memory space that contains the program being executed.
* Register memory: a word-addressed read-write memory space that contains the 32 32-bit registers specified by RISC-V RV32I.
* Data memory: a byte-addressed read-write memory space that accounts for all remaining memory used by the program.

To maintain consistency of accesses to the register and data memories, the Nexus zkVM uses a well-known offline memory checking technique (see [BEG+94](#references)). In this technique, each memory cell is associated with a timestamp which serves as a unique identifier for each memory access. In addition to the timestamps, the memory checking algorithm also maintains read and write sets, whose purpose is to record a trace of the program’s memory access pattern.

The main advantage of offline memory checking techniques is that one does not need to keep track of the actual status of the running memory. Instead, the memory checking algorithm only keeps a trace digest of memory accesses, which is inexpensive and can be updated at a cost that is independent of the size of the memory.

For program memory, a simpler memory checking technique can be used to maintain the consistency of the memory accesses. Instead of keeping a timestamp for each memory cell, it suffices to associate a counter to each memory cell to keep track of the number of times that each cell has been read.

In this version of the Nexus zkVM, the computation of the digest is implemented using logarithmic derivatives (also known as logups). See [EKRN24](#references) and [Hab22](#references).

We discuss some of the constraints for these components when we describe our [example](/zkvm/specifications/proving-an-example). Full details of all the constraints for these components can be found in:

* Section 5 of the specification for register memory: <https://docs.nexus.xyz/zkvm/specifications/zkvm-overview>
* Section 6 of the specification for program memory: <https://docs.nexus.xyz/zkvm/specifications/zkvm-overview>
* Section 7 of the specification for data memory: <https://docs.nexus.xyz/zkvm/specifications/zkvm-overview>

## References

* [BEG+94](https://doi.org/10.1007/BF01185212) Manuel Blum, William S. Evans, Peter Gemmell, Sampath Kannan, and Moni Naor. “Checking the correctness of memories”. Algorithmica, 12(2/3):225–244, 1994.
* [EKRN24](https://eprint.iacr.org/2022/510) Liam Eagen, Sanket Kanjalkar, Tim Ruffing, and Jonas Nick. “Bulletproofs++: Next Generation Confidential Transactions via Reciprocal Set Membership Arguments”. In EUROCRYPT 2024.
* [Hab22](https://eprint.iacr.org/2022/1530) Ulrich Haböck. “Multivariate lookups based on logarithmic derivatives”. In: Cryptology ePrint Archive (2022).

Related pages:

* [Proving the CPU](/zkvm/specifications/proving-the-cpu)
* [Proving Instructions](/zkvm/specifications/proving-instructions)
* [Nexus zkVM overview](/zkvm/specifications/the-complete-specification)
* Example: <https://docs.nexus.xyz/zkvm/specifications/example>


# Proving — An Example

In order to illustrate how these different components work together, let us consider an example in which the program counter pc points to memory location 0x00000004, containing the binary encoding of the instruction `ADDI x10 x8 3`. Moreover, for the sake of this example, let us assume the following about the state of the VM.

* Current clock cycle: 256
* Current trace row: 255
* Total number of rows: 2^16
* R\[x8] = 0x000000FF was last updated with timestamp 323
* R\[x10] = 0x00000005 was last updated with timestamp 77
* Prog\[0x00000004] has been accessed 222 times before the current clock cycle

In the following we describe the relevant trace columns associated with each component, their expected values at row 255 associated with the current clock cycle 256, and the associated constraints that they must satisfy.

### CPU Component Trace Columns and Constraints

In order to verify the correct execution of the `ADDI x10 x8 3` instruction at clock cycle 256, the CPU component will perform the following operations:

* Ensure a correct state transition;
* Fetch the instruction `ADDI x10 x8 3` from the program memory component;
* Decode the contents of the instruction and check the correctness of its format;
* Read contents of register x8 from the register memory component;
* Interact with the execution component to execute the instruction `ADDI x10 x8 3`; and
* Update the contents of register x10 based on the output of the execution component.

Let us now show how each of these operations is performed.

#### Ensuring a Correct State Transition

The CPU component ensures the state transition is performed correctly to guarantee correct ordering of instructions. It performs these checks:

{% stepper %}
{% step %}

### Verify program counter continuity

* Verify that the program counter in the present row matches the value of the next program counter from the preceding row (accounting for limb decomposition).
* Transition constraints (comparing two limbs at a time):
  * (1 - is\_first\[i]) \* (1 - is\_pad\[i]) \* (pc(1)\[i] + pc(2)\[i] \* 2^8 - pc\_next(1)\[i-1] - pc\_next(2)\[i-1] \* 2^8) = 0
  * (1 - is\_first\[i]) \* (1 - is\_pad\[i]) \* (pc(3)\[i] + pc(4)\[i] \* 2^8 - pc\_next(3)\[i-1] - pc\_next(4)\[i-1] \* 2^8) = 0

For our concrete state at row 255:

* i = 255
* pc(1)\[255] = 0x04, pc(2)\[255] = 0x00, pc(3)\[255] = 0x00, pc(4)\[255] = 0x00
* is\_pad\[255] = 0, is\_first\[255] = 0

Therefore pc\_next limbs on row 254 must equal 0x04, 0x00, 0x00, 0x00 respectively.
{% endstep %}

{% step %}

### Check clock update correctness

* Transition constraints for clk\[i] for row i > 0:
  * Use clk\_carry(1), clk\_carry(2) for carries.
  * Adding two limbs at a time:
    * clk(1)\[i] + clk(2)\[i] \* 2^8 + clk\_carry(1)\[i] \* 2^16 = clk(1)\[i-1] + clk(2)\[i-1] \* 2^8 + 1
    * clk(3)\[i] + clk(4)\[i] \* 2^8 + clk\_carry(2)\[i] \* 2^16 = clk(3)\[i-1] + clk(4)\[i-1] \* 2^8 + clk\_carry(1)\[i]
  * Enforce clk\_carry(j) in {0,1} via (clk\_carry(j))\*(1 - clk\_carry(j)) = 0
  * Range-check clk(j) ∈ \[0, 2^8 - 1] for each limb.

For our state, to increment clock from 255 → 256 it must hold that on row 254:

* clk(1)\[254] = 0xFF, clk(2)\[254] = 0x00, clk(3)\[254] = 0x00, clk(4)\[254] = 0x00
* clk\_carry(1)\[255] = 0, clk\_carry(2)\[255] = 0
  {% endstep %}

{% step %}

### Prevent padding rows from being followed by non-padding rows

* Enforce: (1 - is\_first\[i]) \* (1 - is\_pad\[i]) \* (is\_pad\[i-1]) = 0

With is\_pad\[255] = 0 and is\_first\[255] = 0, this implies is\_pad\[254] = 0.
{% endstep %}
{% endstepper %}

#### Fetching the Instruction

The CPU must read the instruction stored at the memory location pointed by pc and ensure pc is memory-aligned (multiple of 4).

Remark: Whenever constraints involve columns restricted to the same row, we omit explicit \[i] index for readability (values below apply to row 255 unless otherwise noted).

* The CPU interaction with program memory is captured by ReadProg interface with parameters (pc, clk) to obtain instr\_val. In the trace, instr\_val and pc are shared between CPU and program memory.

As a result at row 255:

* instr\_val(1) = Prog\[0x00000004] = 0b00010011
* instr\_val(2) = Prog\[0x00000005] = 0b00000101
* instr\_val(3) = Prog\[0x00000006] = 0b00110100
* instr\_val(4) = Prog\[0x00000007] = 0b00000000

Binary encoding breakdown for `ADDI x10 x8 3`:

* Bits 0–6: 0b0010011 (ADDI constant)
* Bits 7–11: 0b01010 → destination register x10 (op\_a)
* Bits 12–14: 0b000 (ADDI constant)
* Bits 15–19: 0b01000 → source register x8 (op\_b)
* Bits 20–31: 0b000000000011 → immediate 3 (op\_c)

Memory alignment constraint for pc:

* pc\_aux(1) \* 4 - pc(1) = 0
* pc\_aux(1) ∈ \[0, 2^6 - 1]

For this example, set pc\_aux(1) = 0x01 to show pc is multiple of 4.

#### Decoding the Instruction

The prover provides auxiliary values (advices) to help verify the binary encoding:

* op\_a = destination register (10)
* op\_b = source register (8)
* op\_c = immediate (3)
* op\_b\_flag = 1 (operand b used)
* imm\_c = 1 (operand c is immediate)
* is\_add = 1 (selector for ADD/ADDI)
* is\_alu\_imm\_no\_shift = 1
* is\_type\_i = 1
* is\_pad = 0, is\_first = 0, is\_last = 0

Operand decomposition advices (bits split across limbs):

* op\_a0 = 0 (bit 0 of op\_a)
* op\_a1\_4 = 5 (bits 1–4 of op\_a)
* op\_b0 = 0 (bit 0 of op\_b)
* op\_b1\_4 = 4 (bits 1–4 of op\_b)
* op\_c0\_3 = 3 (bits 0–3 of op\_c)
* op\_c4\_7 = 0 (bits 4–7 of op\_c)
* op\_c8\_10 = 0 (bits 8–10 of op\_c)
* op\_c11 = 0 (bit 11 of op\_c)

Convert the numbered set of constraints used to validate decoding into steps:

{% stepper %}
{% step %}

### 1) Exactly one instruction or padding flag is set

Enforce sum of instruction flags + is\_pad = 1. Since is\_add = 1, all other flags are 0.
{% endstep %}

{% step %}

### 2) op\_b\_flag correctness

Enforce op\_b\_flag = 1 for all instructions except {LUI, AUIPC, JAL, UNIMP}. This is satisfied by is\_add = 1 and op\_b\_flag = 1.
{% endstep %}

{% step %}

### 3) imm\_c correctness

Enforce imm\_c = 1 for all non-ALU instructions (constraint used to ensure correctness across instruction types). Given imm\_c = 1 in our example, constraints are satisfied.
{% endstep %}

{% step %}

### 4) Match instruction flag with opcode

For ADD/ADDI: (is\_add) \* (opcode - ADD) = 0. With is\_add = 1 and opcode set to ADD constant, satisfied.
{% endstep %}

{% step %}

### 5) ALU flags grouping

Define aggregated flags:

* is\_alu = sum of ALU instruction selectors
* is\_alu\_imm\_shift = imm\_c \* (is\_sll + is\_srl + is\_sra)
* is\_alu\_imm\_no\_shift = imm\_c \* (is\_add + is\_slt + is\_sltu + is\_xor + is\_or + is\_and)
* is\_type\_i\_no\_shift = is\_load + is\_alu\_imm\_no\_shift + is\_jalr
* is\_type\_i = is\_load + is\_alu\_imm\_no\_shift + is\_alu\_imm\_shift + is\_jalr

With is\_add = 1 and imm\_c = 1 we get:

* is\_alu = 1
* is\_alu\_imm\_shift = 0
* is\_alu\_imm\_no\_shift = 1
* is\_type\_i\_no\_shift = 1
* is\_type\_i = 1
  {% endstep %}

{% step %}

### 6) Operand decomposition consistency and range checks

* Ensure op\_a0 + op\_a1\_4 \* 2 - op\_a = 0 (when is\_type\_i\_no\_shift = 1)
* Range-check op\_a0 is binary, op\_a1\_4 ∈ \[0, 2^4 - 1]
* Ensure op\_b0 + op\_b1\_4 \* 2 - op\_b = 0
* Range-check op\_b parts
* Ensure op\_c0\_3 + op\_c4\_7 \* 2^4 + op\_c8\_10 \* 2^8 + op\_c11 \* 2^11 - op\_c = 0
* Range-check op\_c parts

With provided operand parts and is\_type\_i\_no\_shift = 1, these constraints hold for the example.
{% endstep %}

{% step %}

### 7) Sign-extension for operand c

Compute c\_val from op\_c using sign-extension constraints across limbs. Since op\_c11 = 0, higher limbs become 0. Prover sets:

* c\_val(1) = 0x03
* c\_val(2) = 0x00
* c\_val(3) = 0x00
* c\_val(4) = 0x00
  {% endstep %}

{% step %}

### 8) Instruction format checks across limbs

* Limb 1: (is\_alu\_imm\_no\_shift) \* (0b0010011 + op\_a0 \* 2^7 - instr\_val(1)) = 0
* Limb 2: (is\_add) \* (imm\_c) \* (op\_a1\_4 + 0b000 \* 2^4 + op\_b0 \* 2^7 - instr\_val(2)) = 0
* Limb 3: (is\_type\_i\_no\_shift) \* (op\_b1\_4 + op\_c0\_3 \* 2^4 - instr\_val(3)) = 0
* Limb 4: (is\_type\_i\_no\_shift) \* (op\_c4\_7 + op\_c8\_10 \* 2^4 + op\_c11 \* 2^7 - instr\_val(4)) = 0

With the previously set flags and operand parts, these constraints are satisfied for the given instr\_val limbs.
{% endstep %}
{% endstepper %}

#### Reading the Contents of Register x8

The interaction with register memory is captured by ReadReg interface with parameters (op\_b, clk, 1) where 1 indicates this is source register reg1. In the trace, fields are shared between CPU and register memory.

As a result of the interaction:

* reg1\_addr = op\_b
* b\_val limbs are set equal to reg1\_val\_cur limbs

Given the assumption R\[x8] = 0x000000FF before execution, the limbs are:

* b\_val(1) = reg1\_val\_cur(1) = 0xFF
* b\_val(2) = reg1\_val\_cur(2) = 0x00
* b\_val(3) = reg1\_val\_cur(3) = 0x00
* b\_val(4) = reg1\_val\_cur(4) = 0x00

#### Executing the Instruction

The CPU calls the execution component via exec(pc, opcode, a\_val, b\_val, c\_val) to obtain pc\_next. For ADD, execution updates a\_val.

After execution (ADDI x10 x8 3 → x10 := x8 + 3), the limbs are set as:

* a\_val(1) = 0x02
* a\_val(2) = 0x01
* a\_val(3) = 0x00
* a\_val(4) = 0x00

pc is incremented by 4, so:

* pc\_next(1) = 0x08
* pc\_next(2) = 0x00
* pc\_next(3) = 0x00
* pc\_next(4) = 0x00

(These a\_val limbs follow from b\_val = 0x000000FF and c\_val = 0x00000003.)

#### Updating the contents of register x10

To ensure x0 stays zero, CPU computes a\_val\_effective:

* a\_val\_effective = a\_val when op\_a ≠ 0, otherwise 0.
* Use auxiliary a\_val\_effective\_flag and supporting auxiliaries to enforce this (including multiplicative inverse aux variables).
* Enforce a\_val\_effective\_flag ∈ {0,1} and relation a\_val(limb) \* a\_val\_effective\_flag = a\_val\_effective(limb).

For op\_a = x10 (non-zero) the prover sets:

* a\_val\_effective\_flag = 1
* a\_val\_effective\_flag\_aux = 1 (non-zero aux)
* a\_val\_effective\_flag\_aux\_inv = appropriate inverse
* a\_val\_effective(limbs) = a\_val(limbs) = 0x02, 0x01, 0x00, 0x00 respectively

Then CPU interacts with register memory via WriteReg(op\_a, a\_val\_effective, clk, 3) to write to reg3 (destination). As a result reg3\_val\_cur limbs (for reg3\_addr = op\_a = x10) become:

* reg3\_val\_cur(1) = 0x02
* reg3\_val\_cur(2) = 0x01
* reg3\_val\_cur(3) = 0x00
* reg3\_val\_cur(4) = 0x00

### Execution Component Trace Columns and Constraints

To verify the ADD execution, the execution component enforces carry-handling constraints across limbs and that helper carries are binary.

Carry handling for ADD (per limb):

* (is\_add) \* (op\_a\_val(1) + h\_carry(1) \* 2^8 - op\_b\_val(1) - op\_c\_val(1)) = 0
* (is\_add) \* (op\_a\_val(2) + h\_carry(2) \* 2^8 - op\_b\_val(2) - op\_c\_val(2) - h\_carry(1)) = 0
* (is\_add) \* (op\_a\_val(3) + h\_carry(3) \* 2^8 - op\_b\_val(3) - op\_c\_val(3) - h\_carry(2)) = 0
* (is\_add) \* (op\_a\_val(4) + h\_carry(4) \* 2^8 - op\_b\_val(4) - op\_c\_val(4) - h\_carry(3)) = 0

And enforce (is\_add) \* h\_carry(j) \* (1 - h\_carry(j)) = 0 for each helper carry.

Given b\_val = 0x000000FF and c\_val = 0x00000003, to satisfy these constraints:

* a\_val limbs = 0x02, 0x01, 0x00, 0x00
* h\_carry(1) = 1, h\_carry(2) = 0, h\_carry(3) = 0, h\_carry(4) = 0

Next, execution determines whether pc is incremented by 4 (is\_pc\_inc\_std). It enforces:

* (is\_alu + is\_load + is\_type\_s + is\_type\_u + is\_type\_sys \* (1 - is\_sys\_halt) - is\_pc\_inc\_std) = 0

If is\_pc\_inc\_std = 1 then pc\_next must equal pc + 4, handled limb-wise with pc\_carry auxiliaries:

* (is\_pc\_inc\_std) \* (pc\_next(1) + pc\_next(2) \* 2^8 + pc\_carry(1) \* 2^16 - pc(1) - pc(2) \* 2^8 - 4) = 0
* (is\_pc\_inc\_std) \* (pc\_next(3) + pc\_next(4) \* 2^8 + pc\_carry(2) \* 2^16 - pc(3) - pc(4) \* 2^8 - pc\_carry(1)) = 0
* Enforce pc\_carry(j) binary via (is\_pc\_inc\_std) \* pc\_carry(j) \* (1 - pc\_carry(j)) = 0

For pc = 0x00000004, this yields pc\_next = 0x00000008 and pc\_carry(1) = pc\_carry(2) = 0.

### Program Memory Component Trace Columns and Constraints

The program memory component uses offline memory checking (simplified for read-only memory) with a counter per memory cell tracking read counts. Trace elements include:

* pc (word-aligned base address for instruction)
* instr\_val(1..4): instruction word bytes at pc, pc+1, pc+2, pc+3
* prog\_ctr\_prev (4 limbs): previous counter for base address pc
* prog\_ctr\_cur (4 limbs): current counter for base address pc
* prog\_read\_digest: digest of read set (logup)
* prog\_write\_digest: digest of write set (logup)

Actions per read:

* Check counter update correctness
* Verify read/write set digests updated correctly

#### Enforcing correct update of access counters

Enforce prog\_ctr\_cur = prog\_ctr\_prev + 1 using prog\_ctr\_carry auxiliaries across limbs. Carry bits must be binary.

Given Prog\[0x00000004] was accessed 222 times before current clock, set:

* prog\_ctr\_prev = 222 → limbs: prog\_ctr\_prev(1) = 2, prog\_ctr\_prev(2..4) = 0
* prog\_ctr\_cur = 223 → limbs: prog\_ctr\_cur(1) = 3, prog\_ctr\_cur(2..4) = 0
* prog\_ctr\_carry(1..4) = 0

#### Enforcing correct update of read- and write-set digests

Let fp(pc, instr\_val, prog\_ctr) be a fingerprint function (uses verifier-chosen β). Using random α chosen by verifier, enforce transition constraints for i > 0:

* prog\_read\_digest\[i] - prog\_read\_digest\[i-1] = 1 / (fp(pc\[i], instr\_val\[i], prog\_ctr\_prev\[i]) + α)
* prog\_write\_digest\[i] - prog\_write\_digest\[i-1] = 1 / (fp(pc\[i], instr\_val\[i], prog\_ctr\_cur\[i]) + α)

For row 255 with known pc and instr\_val limbs, these constraints must hold with the corresponding prog\_ctr\_prev and prog\_ctr\_cur.

Remark: instr\_val, prog\_ctr\_prev and prog\_ctr\_cur limbs also have range checks in the formal spec; the values above satisfy those ranges.

### Register Memory Component Trace Columns and Constraints

The register memory component uses offline memory checking for read/write memory and associates a timestamp per cell. It also uses logups for read/write set digest consistency. Each access maintains tuples of (reg\_addr, reg\_val\_prev, reg\_val\_cur, reg\_ts\_prev, reg\_ts\_cur). Up to three register addresses can be accessed in one execution cycle; the trace contains three such sets.

Trace elements include:

* clk
* reg1\_addr, reg2\_addr, reg3\_addr
* reg1\_val\_cur, reg2\_val\_cur, reg3\_val\_cur (32-bit values)
* reg1\_ts\_cur, reg2\_ts\_cur, reg3\_ts\_cur (current timestamps)
* reg1\_val\_prev, reg2\_val\_prev, reg3\_val\_prev
* reg1\_ts\_prev, reg2\_ts\_prev, reg3\_ts\_prev
* reg\_read\_digest, reg\_write\_digest
* reg1\_accessed, reg2\_accessed, reg3\_accessed flags (indicate whether each set is used)

Register memory enforces:

1. Current timestamps for reg1/2/3 satisfy:
   * reg1\_ts\_cur = 3 \* clk - 2
   * reg2\_ts\_cur = 3 \* clk - 1
   * reg3\_ts\_cur = 3 \* clk
2. Previous timestamps precede current timestamps:
   * regj\_ts\_prev ∈ {0, …, regj\_ts\_cur - 1}
3. Read/write digest updates via logup contributions (described below).

Remark: reg1\_addr, reg2\_addr, reg3\_addr should be accessed in order and only reg3\_addr can be modified during a clock cycle.

#### Enforcing the Correct Update of Read- and Write-Set Digests (Registers)

Let fp(reg\_addr, reg\_val, reg\_ts) be a fingerprint function (uses verifier-chosen β). For row index i > 0 and random α:

* reg\_read\_digest\[i] - reg\_read\_digest\[i-1] = reg1\_accessed\[i] / (fp(reg1\_addr\[i], reg1\_val\[i], reg1\_ts\_prev\[i]) + α)
  * reg2\_accessed\[i] / (fp(reg2\_addr\[i], reg2\_val\[i], reg2\_ts\_prev\[i]) + α)
  * reg3\_accessed\[i] / (fp(reg3\_addr\[i], reg3\_val\[i], reg3\_ts\_prev\[i]) + α)
* reg\_write\_digest\[i] - reg\_write\_digest\[i-1] = reg1\_accessed\[i] / (fp(reg1\_addr\[i], reg1\_val\[i], reg1\_ts\_cur\[i]) + α)
  * reg2\_accessed\[i] / (fp(reg2\_addr\[i], reg2\_val\[i], reg3\_ts\_cur\[i]) + α)
  * reg3\_accessed\[i] / (fp(reg3\_addr\[i], reg3\_val\[i], reg3\_ts\_cur\[i]) + α)

For this example, the assumptions and derived values for row 255 are:

* R\[x8] = 0x000000FF with last timestamp 323
* R\[x10] = 0x00000005 with last timestamp 77
* clk\[255] = 256
* reg1\_ts\_cur\[255] = 3 \* 256 - 2 = 766
* reg3\_ts\_cur\[255] = 3 \* 256 = 768
* reg1\_accessed\[255] = 1, reg2\_accessed\[255] = 0, reg3\_accessed\[255] = 1
* R\[x10] updated to 0x00000102 at current clock

To satisfy logup constraints at i = 255, the following must be set consistently (limb decompositions shown):

* reg1\_ts\_prev\[255] limbs: (1) = 32, (2..4) = 0
* reg1\_val\_prev\[255] = reg1\_val\_cur\[255] = 0xFF, 0x00, 0x00, 0x00
* reg1\_ts\_cur\[255] limbs: (1) = 254, (2) = 2, (3..4) = 0 (representation of 766)
* reg3\_ts\_prev\[255] limbs: (1) = 7, (2..4) = 0
* reg3\_val\_prev\[255] limbs: 0x05, 0x00, 0x00, 0x00
* reg3\_ts\_cur\[255] limbs: (1) = 0, (2) = 3, (3..4) = 0 (representation of 768)
* reg3\_val\_cur\[255] limbs: 0x02, 0x01, 0x00, 0x00

With these values, the register read/write digest transition constraints hold for the example.

***

References (kept intact):

* Proving Memory: <https://docs.nexus.xyz/zkvm/specifications/memory-checking>
* Proving Instructions: <https://docs.nexus.xyz/zkvm/specifications/instructions>
* Licensing: <https://docs.nexus.xyz/zkvm/license>

(End of cleaned/import-optimized page content.)


# Licensing

## Licensing

The Nexus zkVM is licensed under the source-available Business Source License (BUSL):\
<https://github.com/nexus-xyz/nexus-zkvm/blob/main/LICENSE>

This license will convert to an open-source dual Apache 2.0 and MIT licensing on February 10th, 2029.


