Skip to content

aryza0x/Nox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NOX

The Privacy-Preserving Perpetual Futures Exchange

Trade perps. Stay private. Powered by Fully Homomorphic Encryption.

Status FHEVM Network License


NOX is the first decentralized perpetuals exchange where position size, direction, liquidation threshold, vault balance, and open interest are all stored on-chain as Fully Homomorphic Encryption (FHE) ciphertext. Liquidations, PnL settlement, and funding rate computation all execute directly on encrypted state — without ever decrypting it on the EVM. Built on Zama's FHEVM, NOX delivers what every other on-chain perpetual protocol structurally cannot: a book that nobody can read.


Table of Contents

  1. Why NOX
  2. Core Innovations
  3. Feature Matrix
  4. Architecture
  5. The FHE Lifecycle of a Position
  6. Smart Contracts
  7. Tech Stack
  8. Repository Layout
  9. Setup & Deployment
  10. Running the Stack
  11. Testing
  12. Testnet
  13. Security & Threat Model
  14. Roadmap
  15. Glossary
  16. Contributing
  17. License & Disclaimer

Why NOX

The Problem

Decentralized perpetual exchanges have proven that on-chain derivatives can rival centralized venues in throughput and liquidity. They have not solved the structural privacy problem of public blockchains. Every position size, leverage ratio, entry price, and liquidation threshold is broadcast to the world the moment it is opened.

Risk Description
Strategy Leakage Sophisticated traders' positions are reverse-engineered by copy-trading bots within seconds of being opened. Months of alpha work become public domain on confirmation.
Mempool Front-Running Searchers observe order intent, sandwich the trade, and capture the spread. MEV extraction on perps runs in the tens of millions of dollars per year.
Liquidation Hunting Whale liquidation prices are visible. Coordinated dumps and pumps trigger cascading liquidations and harvest the resulting fees.
Institutional Lockout Banks, prop desks, and treasury managers are legally and reputationally barred from venues that publish their full position book in real time.

The Solution: Compute on Ciphertext

NOX leverages Fully Homomorphic Encryption (FHE) through Zama's FHEVM. FHE allows arithmetic and comparison operations to run directly on encrypted values, with the result remaining encrypted at every step. The chain validates state transitions; the chain learns nothing.

On NOX:

  • Your position size and direction are stored as euint64 and ebool ciphertext.
  • Your liquidation price is computed homomorphically and stored as euint64 ciphertext.
  • Your vault balance is an encrypted euint64 — invisible even to the market contract.
  • The protocol's long and short open interest are tracked as separate ciphertexts.
  • PnL at close is computed entirely under FHE — FHE.mul, FHE.div, FHE.add, FHE.sub, FHE.select.
  • Liquidation eligibility is decided by FHE.le / FHE.ge running on encrypted thresholds.
  • Compliance status (optional institutional gate) is an encrypted ebool evaluated through FHE.select.

Only the position owner holds the decryption key, managed entirely client-side via the Zama Relayer SDK.

What a Private-by-Design Hyperliquid Looks Like

Hyperliquid proved a thesis that mattered: on-chain perpetuals can be fast, self-custodial, and multi-asset without sacrificing the UX of a centralized exchange. It is the most credible answer to "why are derivatives still trading on Binance?" that DeFi has produced.

But its design — like every other on-chain perp — leaves one structural problem untouched: the order book is public. Bots see exactly where each whale will be liquidated. Institutions cannot participate without doxxing their book.

NOX takes Hyperliquid's three pillars — self-custody, multi-asset, on-chain settlement — and asks: what does this look like when the book is private by default?

Hyperliquid NOX
Self-custody ✅ Non-custodial ✅ Same — encrypted vault
Multi-asset ✅ Dozens of markets at scale ✅ BTC-USDC + ETH-USDC live; same architecture scales
On-chain settlement ✅ All trades on-chain ✅ Same
Real-time prices ✅ Internal oracle ✅ Chainlink AggregatorV3 with staleness guard
Speed ✅ Sub-second matching ⚠️ Slower — FHE adds encrypt + gateway-decrypt latency
Position size ❌ Public on every fill euint64 ciphertext
Direction (long/short) ❌ Public ebool ciphertext
Liquidation threshold ❌ Visible to MEV bots ✅ Only a yes/no boolean ever leaks
Open interest skew ❌ Long/short OI public ✅ Separate euint64 ciphertexts; only post-aggregation imbalance revealed
PnL at close ❌ Trivially derived from public size + price ✅ Computed under FHE; only the final payout is decrypted
Compliance ❌ Public allowlist or none ✅ Optional encrypted-credential gate

NOX explicitly trades raw throughput for cryptographic privacy — the right trade for sophisticated traders, institutions, and market makers for whom the leak of size and direction is far more expensive than the latency of an FHE round trip.

Hyperliquid showed the world that on-chain perps can be serious. NOX shows what they look like when your book is nobody's business — same self-custody, same Chainlink prices, same on-chain settlement, encrypted by default.

NOX is not a Hyperliquid competitor in the gross-volume sense. It is the privacy layer Hyperliquid is structurally missing — something HL cannot ship without rebuilding its execution engine on FHEVM. The relationship is additive: HL completes the speed/UX side of the on-chain-perps story; NOX completes the privacy side.


Core Innovations

NOX is the first protocol to integrate every layer of a perpetual exchange under FHE. Each item below is a primitive that does not exist in any other on-chain perp.

  1. Encrypted PnL Settlement — PnL computed entirely on ciphertext at close (FHE.mul, FHE.div, FHE.select). Size and direction are never revealed; only the final payout amount exits the encrypted domain.

  2. Homomorphic LiquidationsFHE.le(currentPrice, encLiquidationPrice) runs on ciphertext. The liquidation threshold is never revealed — only a yes/no boolean reaches the keeper.

  3. Encrypted Open Interest — Long and short OI are separate euint64 ciphertexts updated via FHE.select. The directional skew of the entire book — the most valuable signal in TradFi market-making — is unreadable on-chain.

  4. Two-Phase Funding RateprepareFundingUpdate() computes the encrypted OI imbalance; only the delta and sign are gateway-decrypted. Raw OI figures are never exposed.

  5. Encrypted Position NFTs (ERC-721) — Each position is a tradeable NFT. The encrypted size, direction, and liquidation price stay in contract storage. On transfer, _update re-grants the FHE ACL to the new owner — they immediately gain decrypt rights over what they just purchased.

  6. Privacy-Preserving Compliance — An optional IComplianceOracle issues users an encrypted ebool isApproved. Non-approved users receive a silent zero-size position that returns their margin on close — no revert, no flag, no observable difference on-chain. Regulators get enforcement; traders get privacy.

  7. Client-Side Decryption — All decryption happens in the browser. A signed EIP-712 message authorizes the Zama Gateway to release ciphertext keys for that session. Plaintext never touches a server or the chain.


Feature Matrix

Feature Implementation Encrypted?
Vault balance euint64 in NoxVault
Position size euint64 in NoxMarket
Position direction ebool in NoxMarket
Liquidation price euint64, via FHE.select
PnL computation Homomorphic at close
Open interest (long / short) Separate euint64 ciphertexts
Compliance credential ebool per user
Funding rate (raw OI) Encrypted skew, gateway-decrypted delta Raw OI: ✅ / Rate: ❌
Final close payout Gateway-decrypted, paid to vault ❌ (minimum necessary reveal)
Margin (collateral) uint256 ❌ (public)
Entry price uint256 from Chainlink ❌ (public)
Position ownership ERC-721 NFT ❌ (public)
Price oracle Chainlink AggregatorV3 + staleness guard ❌ (public)
Multi-asset markets NoxMarketBTC + NoxMarketETH
Keeper automation TypeScript bot

Architecture

                       ┌──────────────────────────────────────────┐
                       │             Browser (Client)             │
                       │  • Encrypts size + direction (WASM)      │
                       │  • Decrypts own position (EIP-712)       │
                       │  • Signs transactions (MetaMask)         │
                       └──────────────────┬───────────────────────┘
                                          │ ciphertext + input proof
                                          ▼
                ┌─────────────────────────────────────────────────────┐
                │              Ethereum Sepolia (FHEVM)               │
                │                                                     │
                │  ┌─────────────┐    ┌─────────────────────────┐    │
                │  │  NoxVault   │◀───│      NoxMarket-BTC       │◀───┼── Chainlink BTC/USD
                │  │  encrypted  │    │  encrypted positions,    │    │
                │  │  balances   │    │  FHE PnL, ERC-721 NFTs   │    │
                │  └──────▲──────┘    └────────────┬────────────┘    │
                │         │                        │                  │
                │         │           ┌────────────▼────────────┐    │
                │         │           │   NoxFundingRate-BTC    │    │
                │         │           │   encrypted long/short  │    │
                │         │           │   OI (euint64)          │    │
                │         │           └─────────────────────────┘    │
                │         │                                           │
                │  ┌──────┴──────┐    ┌─────────────────────────┐    │
                │  │  NoxVault   │◀───│      NoxMarket-ETH       │◀───┼── Chainlink ETH/USD
                │  │  (shared)   │    └────────────┬────────────┘    │
                │  └─────────────┘                 │                  │
                │                      ┌───────────▼─────────────┐   │
                │                      │   NoxFundingRate-ETH    │   │
                │                      └─────────────────────────┘   │
                │                                                     │
                │  ┌──────────────────────────────────────────────┐   │
                │  │   NoxComplianceOracle (optional)             │   │
                │  │   encrypted ebool per address                │   │
                │  └──────────────────────────────────────────────┘   │
                └──────────────────────────┬──────────────────────────┘
                                           │
                               ┌───────────▼───────────┐
                               │     Zama Gateway      │
                               │  selectively decrypts │
                               │  publicly-decryptable │
                               │  ciphertexts only     │
                               └───────────┬───────────┘
                                           │
                               ┌───────────▼───────────┐
                               │    NOX Keeper Bot     │
                               │  • Sync Chainlink     │
                               │  • Request liquidations│
                               │  • Execute close & liq│
                               │  • Update funding rate│
                               └───────────────────────┘

Data flow summary:

  1. Browser encrypts trade inputs locally via WASM TFHE.
  2. Contracts compute on ciphertext — storage is opaque bytes32 handles.
  3. Gateway decrypts only values explicitly marked FHE.makePubliclyDecryptable (a payout amount or a shouldLiquidate boolean) — never raw inputs.
  4. Keeper relays gateway results back as plaintext arguments to phase-2 functions.
  5. Client decrypts its own position in-browser via signed EIP-712 authorization.

The FHE Lifecycle of a Position

Each phase is designed so the smallest possible amount of data ever leaves the encrypted domain.

Phase 1 — Encrypt In-Browser

The user's size and direction are encrypted locally before the transaction is signed:

const sizeInput = instance.createEncryptedInput(marketAddress, userAddress);
sizeInput.add64(sizeUsd);
const encSize = await sizeInput.encrypt();       // → ciphertext handle + ZK input proof

const dirInput = instance.createEncryptedInput(marketAddress, userAddress);
dirInput.addBool(isLong);
const encDir = await dirInput.encrypt();

Two separate encrypted inputs are produced — one euint64 for size, one ebool for direction. Each carries a ZK input proof binding it to the encrypting wallet and destination contract.

Phase 2 — Submit & Store

NoxMarket.openPosition validates the proofs, optionally gates on the compliance oracle, computes the encrypted liquidation price for both directions via FHE.select, and stores the full struct:

struct EncryptedPosition {
    euint64 size;             // encrypted position size (8 decimals)
    ebool isLong;             // encrypted direction
    euint64 liquidationPrice; // encrypted threshold, selected per direction
    uint256 margin;           // plaintext USDC margin (6 decimals)
    uint256 entryPrice;       // public Chainlink price at open
    address owner;
    bool isOpen;
}

The contract grants FHE ACL permissions to itself and the trader. An ERC-721 NFT is minted. Encrypted OI is updated atomically on the funding rate tracker.

Phase 3 — Live State

While the position is open, nothing readable changes in the encrypted state. Size, direction, liq price, and PnL all appear as ●●● in the UI. The trader can decrypt their own position at any time via a one-time EIP-712 signature — the plaintext is only ever held in browser memory for that session.

Phase 4 — Close (Two-Phase FHE PnL)

Phase 4aclosePosition(positionId) (trader):

The contract computes encrypted PnL for both long and short branches, selects the correct branch with FHE.select(pos.isLong, longPayout, shortPayout), and marks only the final payout publicly decryptable. OI is removed from the funding rate tracker.

Phase 4bexecuteClose(positionId, payoutPlain) (keeper, after gateway decryption):

The vault is credited and the position NFT is burned. Only the final payout amount is ever revealed. Size and direction stay encrypted.

Phase 5 — Liquidation (Two-Phase FHE Comparison)

Phase 5arequestLiquidation(positionId) (anyone):

ebool longTriggered   = FHE.le(currentPrice, pos.liquidationPrice);
ebool shortTriggered  = FHE.ge(currentPrice, pos.liquidationPrice);
ebool shouldLiquidate = FHE.select(pos.isLong, longTriggered, shortTriggered);
shouldLiquidate = FHE.makePubliclyDecryptable(shouldLiquidate);

Phase 5bexecuteLiquidation(positionId, shouldLiquidatePlain) (keeper):

If the gateway returned true, the trader's margin is split — 5% to the liquidator as a bonus, the remainder back to the trader's vault. OI is removed and the NFT is burned.

The liquidator earns a reward without ever learning the trader's threshold or size. The chain learns exactly one bit per liquidation.


Smart Contracts

All Solidity sources are in packages/contracts/contracts/.

NoxVault.sol

Shared margin treasury. One vault serves all markets.

Function Description
deposit(uint256 amount) Pulls ERC-20, adds to encrypted balance, updates plaintext mirror.
withdraw(uint256 amount) Reduces encrypted balance, transfers ERC-20 back.
deductMargin(address, uint256) Market-only. Locks margin into a position.
creditMargin(address, uint256) Market-only. Returns funds on close / liquidation.
getEncryptedBalance(address) → euint64 Returns the ciphertext handle. ACL-gated.
addMarket / removeMarket Owner-only. Authorizes market contracts.

The vault keeps a plaintext lockedBalance[user] mirror for insufficient-balance reverts and withdrawal limits. The encrypted _encryptedBalance[user] is what's exposed externally.

NoxMarket.sol

Core trading engine. One instance per asset. Inherits ERC721Enumerable.

Key constants: MIN_MARGIN_BPS = 500 (5% liq threshold), LIQUIDATOR_BONUS_BPS = 500 (5% keeper reward), MAX_PRICE_STALENESS = 1 hour.

openPosition(externalEuint64 size, externalEbool isLong, uint256 margin, bytes sizeProof, bytes dirProof)
closePosition(uint256 positionId)
executeClose(uint256 positionId, uint64 payoutPlain)          // owner-only (keeper)
requestLiquidation(uint256 positionId)
executeLiquidation(uint256 positionId, bool shouldLiquidate)  // owner-only (keeper)
syncPrice()                                                    // pulls Chainlink latestRoundData
updatePrice(uint256 newPrice)                                  // owner-only (demo / emergency)
setFundingRate(address)                                        // wire NoxFundingRate
setComplianceOracle(address)                                   // wire optional compliance

The _update ERC-721 hook is overridden to call FHE.allow(handle, newOwner) on every encrypted field whenever the NFT is transferred. The buyer immediately gains decrypt rights; the seller's view ceases to function.

NoxFundingRate.sol

Tracks encrypted long/short OI and publishes the funding rate each interval.

addOI(euint64 size, ebool isLong)               // market-only, on openPosition
removeOI(euint64 size, ebool isLong)            // market-only, on close/liquidate
prepareFundingUpdate()                           // anyone (after interval) or owner anytime
setFundingRate(uint64 diff, bool longsDominate) // owner-only (keeper, post-gateway)

Internal pattern: FHE.select(isLong, size, zero) adds size to long OI only if isLong=true. This keeps both the per-position direction and the aggregate skew private.

Funding formula: rate = min(diffPlain / 1e6, 100) — 1 bps per $1M imbalance, capped at 100 bps. Positive means longs pay shorts.

NoxComplianceOracle.sol

Optional institutional gate. Stores one ebool _approvals[user] per address.

setApproval(address user, externalEbool encryptedStatus, bytes proof)  // admin-only
isApproved(address user) → ebool                                        // ciphertext, ACL-gated

When wired to a market, openPosition evaluates FHE.select(isApproved, size, 0). Non-approved users silently receive a zero-size position — no revert, no event, no observable difference on-chain. The oracle enforces; the chain says nothing.

Supporting Files

  • NoxSettlement.sol — Legacy plaintext settlement contract, retained for reference. Active flow is the FHE PnL path inside NoxMarket.
  • interfaces/IComplianceOracle.sol — Decouples NoxMarket from any specific oracle implementation.
  • mocks/MockUSDC.sol, mocks/MockV3Aggregator.sol — Used in the local Hardhat suite.

Tech Stack

Smart Contracts

  • Solidity 0.8.27, Cancun EVM target
  • Hardhat 2.26 + hardhat-deploy
  • OpenZeppelin 5.4Ownable, ReentrancyGuard, ERC721Enumerable, SafeERC20
  • Chainlink Contracts 1.5AggregatorV3Interface
  • @fhevm/solidity 0.11.1, @fhevm/hardhat-plugin 0.4.2
  • TypeChain (ethers-v6 target)

FHE Infrastructure

  • Zama FHEVM Gateway (Sepolia) for selective decryption
  • @zama-fhe/relayer-sdk 0.4.1 — browser WASM library for client-side encrypt + decrypt
  • @fhevm/mock-utils 0.4.2 — synchronous mock gateway for Hardhat tests

Frontend

  • Next.js 14 (App Router), React 18, TypeScript 5
  • Wagmi 2 + Viem 2 — type-safe contract reads/writes
  • TanStack Query 5 — wallet state caching
  • Tailwind CSS 3 — NOX yellow #F5C600 on near-black #0D0C09
  • Lightweight Charts 4 — price chart rendering

Off-Chain

  • Node.js ≥ 20, pnpm 10, ethers v6 keeper script
  • Optional deploy to Railway / Render for persistent uptime

Repository Layout

private-perps/
├── package.json                       # workspace root
├── pnpm-workspace.yaml
├── FEATURES.md                        # roadmap & differentiation strategy
├── DEMO.md                            # 8-minute demo script
├── TESTING_GUIDE.md                   # step-by-step UI test walkthrough
│
├── packages/
│   ├── contracts/                     # @nox/contracts
│   │   ├── contracts/
│   │   │   ├── NoxMarket.sol          # core engine: encrypted positions + FHE PnL
│   │   │   ├── NoxVault.sol           # encrypted margin treasury
│   │   │   ├── NoxFundingRate.sol     # encrypted OI + funding rate
│   │   │   ├── NoxComplianceOracle.sol
│   │   │   ├── NoxSettlement.sol      # legacy reference
│   │   │   ├── interfaces/
│   │   │   └── mocks/
│   │   ├── deploy/                    # 00_vault → 01_market → 02_funding_rate
│   │   ├── deployments/localhost/ + sepolia/
│   │   ├── scripts/
│   │   │   ├── keeper.ts              # production keeper bot
│   │   │   ├── demo-flow.ts
│   │   │   └── sync-prices.ts
│   │   ├── test/
│   │   │   ├── NoxVault.test.ts
│   │   │   ├── NoxMarket.test.ts
│   │   │   ├── NoxMarketCompliance.test.ts
│   │   │   ├── NoxFundingRate.test.ts
│   │   │   └── NoxComplianceOracle.test.ts
│   │   └── hardhat.config.ts
│   │
│   └── frontend/                      # @nox/frontend
│       └── src/
│           ├── app/page.tsx           # landing page
│           ├── app/trade/page.tsx     # trading interface
│           ├── components/            # Header, TradingPanel, PositionsTabs, DemoWidget, ...
│           ├── hooks/                 # useFhevm, useMarket, useVault
│           └── lib/                   # contracts.ts, fhevm.ts, decrypt.ts, wagmi.ts

Setup & Deployment

Prerequisites

  • Node.js ≥ 20, pnpm ≥ 10 (npm install -g pnpm)
  • MetaMask (or any wagmi-compatible wallet)
  • Sepolia ETH for gas — any Sepolia faucet
  • (Optional) Alchemy / Infura key for a reliable RPC endpoint

Clone & Install

git clone https://github.com/your-org/nox-perps.git
cd nox-perps
pnpm install

Configure Environment

cp .env.example .env
# .env (root)
PRIVATE_KEY=0xYOUR_DEPLOYER_PRIVATE_KEY      # testnet only — never commit
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
ETHERSCAN_API_KEY=                            # optional, for hardhat-verify
# packages/frontend/.env.local
NEXT_PUBLIC_SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY

Local Development

# Compile contracts + generate TypeChain bindings
pnpm contracts:compile

# Spin up a local Hardhat node and deploy
pnpm chain             # terminal 1 — hardhat node on :8545
pnpm deploy:local      # terminal 2 — runs 00 → 01 → 02 deploy scripts

# Run the test suite
pnpm contracts:test

# Start the frontend (points at Sepolia by default — update contracts.ts for local)
pnpm dev               # → http://localhost:3000

Sepolia Testnet Deployment

NOX is already live on Sepolia. To redeploy your own instance:

pnpm deploy:sepolia

The three deploy scripts run in sequence:

  1. 00_deploy_vault.ts — uses Zama's mock USDC at 0x9b5Cd13b8eFbB58Dc25A05CF411D8056058aDFfF.
  2. 01_deploy_market.ts — deploys NoxMarketBTC (Chainlink BTC/USD) and NoxMarketETH (Chainlink ETH/USD), wires both to the vault.
  3. 02_deploy_funding_rate.ts — deploys two NoxFundingRate instances and bidirectionally wires them to the markets.

Each script is idempotent and skips already-deployed contracts. Output is written to packages/contracts/deployments/sepolia/.

To verify on Etherscan:

cd packages/contracts
pnpm hardhat verify --network sepolia <address> <constructorArgs...>

Running the Stack

The Keeper Network

The keeper is a stateless TypeScript script that operates the protocol's off-chain critical path. Anyone can run one.

pnpm --filter @nox/contracts run keeper:sepolia
Trigger Action
Polling loop (30s) For each market: if Chainlink price has drifted > 0.5% from currentPrice, call syncPrice().
Polling loop (30s) For each open position: if price has moved > 5% from entry, call requestLiquidation().
CloseRequested event After gateway decrypts the payout, call executeClose(positionId, payoutPlain).
LiquidationRequested event After gateway decrypts shouldLiquidate, call executeLiquidation(positionId, true).

What the keeper never learns: position size, direction, exact liquidation threshold, or the protocol's directional OI skew. It sees only public state — entry prices, plaintext margins, NFT owners — and the minimal gateway outputs (a payout amount or a boolean).

For persistent uptime on testnet, deploy to Railway or Render. A single instance handles both BTC and ETH markets.

Frontend Application

The trading dApp is a Next.js 14 (App Router) application. Two routes:

  • / — Landing page: live ticker, encrypted-positions preview, struct visualisation, "How it works" walkthrough.
  • /trade — Full trading interface: price chart, market selector, trading panel, positions table, stats bar, demo widget.

Key hooks:

  • useFhevm() — Lazily initialises the Zama relayer SDK, bootstraps WASM via initSDK, builds Sepolia config. Falls back to SepoliaConfigV2 if the primary relayer is under load.
  • useMarket(marketId) — All market reads + write helpers (openPosition, closePosition, requestLiquidation, syncPrice, …) for 'btc-usdc' or 'eth-usdc'.
  • useVault()deposit, withdraw, faucet, reactive balance view.

Encryption flow:

const { encryptedSize, encryptedIsLong, sizeProof, directionProof } =
  await encryptPosition(marketAddress, walletAddress, sizeUsd, isLong);

await openPosition({ encryptedSize, encryptedIsLong, marginAmount, sizeProof, directionProof });

Decryption / Reveal PnL flow:

const pos = await getPosition(positionId);
const { size, isLong } = await decryptPosition(
  marketAddress, userAddress, pos.sizeHandle, pos.isLongHandle, signTypedData
);

The decrypted values live only in React component state — never persisted to disk, never sent to a server, never written to the chain.

Why COOP/COEP headers? next.config.js sets Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp to enable crossOriginIsolated mode. This lets the Zama TFHE WASM use SharedArrayBuffer for ~10× encryption speed. Without these headers, encryption falls back to single-threaded WASM.


Testing

pnpm contracts:test
Contract Test File What It Verifies
NoxVault NoxVault.test.ts Encrypted deposit/withdraw, owner-only decryption, market access control, balance math.
NoxMarket NoxMarket.test.ts Position open → deducts margin, encrypted size handle is non-zero, owner can decrypt, third parties cannot. Full close lifecycle. Liquidation lifecycle + 5% bonus. NFT mint / transfer / ACL re-grant / burn.
NoxMarketCompliance NoxMarketCompliance.test.ts Non-approved users get size=0 ciphertext; approved users get their requested size.
NoxFundingRate NoxFundingRate.test.ts OI accumulation, two-phase update, capped rate formula.
NoxComplianceOracle NoxComplianceOracle.test.ts Default deny, admin issuance updates ciphertext.

FHEVM mock vs. live gateway: In tests, @fhevm/mock-utils resolves FHE.makePubliclyDecryptable synchronously in-process. On Sepolia, the same call enqueues the ciphertext for Zama Gateway decryption (typically 1–2 blocks). The keeper bridges the gap.

Useful test helpers:

// Encrypt an input from a signer
const input = fhevm.createEncryptedInput(contractAddress, signer.address);
input.add64(value);
const { handles, inputProof } = await input.encrypt();

// Decrypt as a specific user (requires prior FHE.allow)
await fhevm.userDecryptEuint(FhevmType.euint64, handle, contractAddress, signer);

Testnet

Live Demo Walkthrough

A complete 8-minute demo script lives in DEMO.md. High-level flow:

  1. Landing page — encrypted size, direction, and liquidation price visible in the UI preview.
  2. Connect wallet — direct Sepolia connection, no backend.
  3. Deposit margin — funds enter the encrypted vault as euint64.
  4. Pick a market — BTC-USDC or ETH-USDC, both live Chainlink-priced.
  5. Open an encrypted long — MetaMask calldata shows ciphertext + ZK input proofs, never plaintext.
  6. Inspect positions — Side, Size, Liq Price, PnL all show ●●●.
  7. Reveal PnL — sign EIP-712, cells decrypt in-browser only.
  8. Trigger liquidation — crash the oracle price, requestLiquidation runs FHE.le on ciphertext, keeper executes.
  9. Funding rate — long/short OI encrypted; only the imbalance direction and magnitude are ever revealed.

CLI-only walkthrough:

pnpm demo:sepolia

Deployed Addresses (Sepolia)

Contract Address
NoxVault 0x211AD06B4B5d7F8eBD204B2Ee6CaC239fE1Ab794
NoxMarketBTC 0x93D1b72323b3B012cE2a43F41A9B02e120581517
NoxMarketETH 0xD2ecbFa44712Bf0F7223aD8604c40d0c6E4b5513
Mock USDC (Zama) 0x9b5Cd13b8eFbB58Dc25A05CF411D8056058aDFfF
Chainlink BTC/USD 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43
Chainlink ETH/USD 0x694AA1769357215DE4FAC081bf1f309aDC325306

Need test USDC? Call faucet() on the mock USDC contract — it mints 10,000 mUSDC to the caller.


Security & Threat Model

NOX is testnet software. It has not been audited. Do not use with real funds.

What FHE Protects

  • ✅ Position size and direction — never visible on-chain.
  • ✅ Liquidation threshold — revealed only as a yes/no boolean.
  • ✅ Vault balances — encrypted euint64, only the user can decrypt.
  • ✅ Aggregate OI directional skew — only post-aggregation imbalance is exposed.
  • ✅ Compliance status — encrypted ebool; the protocol cannot enumerate the allowlist.

What FHE Does Not Protect

  • Margin amount — plaintext in openPosition calldata. Visible on Etherscan.
  • Entry price — plaintext (it's the public oracle price at open).
  • Position ownership — the ERC-721 NFT is publicly transferable and visible.
  • Final close payout — gateway-decrypted (the minimum necessary to credit the vault).
  • Transaction timing — open / close / liquidation are still public events with timestamps.

Trust Assumptions

  • Zama Gateway — trusted to honour FHE.makePubliclyDecryptable correctly and enforce ACLs. NOX inherits Zama's threshold-decryption security model.
  • Chainlink oracles — NOX adds a 1-hour staleness guard but trusts feed integrity.
  • Owner privilegesexecuteClose and executeLiquidation are onlyOwner. Production deployments should transfer ownership to a multisig and eventually replace these with a permissionless keeper auction.

Known Limitations

  • The lockedBalance plaintext mirror in NoxVault exposes total margin per user across all positions. Required for cheap reverts but represents a partial privacy regression.
  • entryPrice + margin together allow an observer to infer plausible notional ranges given typical leverage. The encrypted size field hides the exact figure, not the upper bound.
  • FHE division has limited precision. PnL math normalises from 8-decimal size units to 6-decimal USDC units via FHE.div(pnl, 100) to keep all values within euint64 range.
  • A malicious keeper owner could stall executeClose, griefing a user. Mitigation: permissionless executor with an on-chain dispute window (roadmap item).

Roadmap

NOX optimizes for depth over breadth — every item tightens the FHE narrative.

Shipped

  • Encrypted vault balances
  • Encrypted position size, direction, liquidation price
  • Two-phase encrypted liquidations
  • Two-phase encrypted PnL settlement at close
  • Encrypted long/short OI + two-phase funding rate
  • Chainlink AggregatorV3 oracle + staleness guard
  • Multi-market: BTC-USDC + ETH-USDC, shared vault
  • ERC-721 position NFTs with FHE ACL re-granting on transfer
  • Privacy-preserving compliance oracle
  • Hardhat test suite (vault, market, compliance, funding rate, oracle)
  • Production keeper script
  • Next.js frontend with live encrypt/decrypt + EIP-712 reveal
  • Sepolia deployment

Next

  • Encrypted-margin variant (eliminate last plaintext leak in the vault)
  • Permissionless keeper auction (replace onlyOwner on phase-2 functions)
  • Market-maker rebates with encrypted volume tracking
  • Cross-margin maintenance computed under FHE
  • Mainnet deployment after audit
  • Position-NFT secondary marketplace (sell encrypted exposure)

Glossary

Term Definition
FHE Fully Homomorphic Encryption. Arithmetic and comparisons run on ciphertext; the output stays encrypted.
FHEVM Zama's Solidity-compatible EVM with built-in FHE opcodes.
euint64 Encrypted unsigned 64-bit integer — backs position size, vault balance, OI.
ebool Encrypted boolean — backs direction, compliance status, comparison results.
externalEuint64 / externalEbool Calldata types for ciphertext arriving from outside the chain. Materialized via FHE.fromExternal() + ZK input proof.
Handle The opaque bytes32 identifier a contract holds for a ciphertext.
Input Proof ZK proof binding an encrypted input to the encrypting wallet and destination contract.
Gateway Zama service that performs threshold decryption for ciphertexts marked publicly decryptable.
ACL Access Control List — determines which addresses may decrypt a ciphertext. Set via FHE.allow, FHE.allowThis, FHE.allowTransient.
Public Decryptable A ciphertext flagged via FHE.makePubliclyDecryptable for gateway resolution — the protocol's selective-reveal primitive.
EIP-712 Signature standard used by the Zama SDK to authorize in-browser decryption of a user's own ciphertexts.
Two-Phase Action Any operation requiring a gateway round-trip: phase 1 marks the result publicly decryptable, phase 2 (keeper) consumes the plaintext and finalises state.

Contributing

Contributions are welcome. Priority areas:

  • Additional markets (SOL, ARB, AVAX) wired to Chainlink feeds.
  • Hardening the keeper into a permissionless multi-runner.
  • Audit-style reviews of the FHE PnL math and overflow guards.
  • Test cases for edge conditions: zero-size approved closes, OI overflow at scale, NFT transfers mid-liquidation.
  • Frontend polish: loading states, error toasts, decryption progress indicators.

Open an issue first for substantial changes. PRs should pass pnpm contracts:test, include test coverage for new contract code, and not expand plaintext leakage without justification.


License & Disclaimer

NOX is released under the MIT License.

Disclaimer. NOX is experimental software deployed on Ethereum Sepolia testnet for research and demonstration purposes. It has not been audited. Do not deploy to mainnet without a comprehensive third-party audit. The authors accept no liability for any loss arising from use of this software. Use at your own risk.


Built with Zama FHEVM. On-chain perpetuals, encrypted by default.

Live App · Demo Script · Feature Roadmap · Testing Guide

About

Privacy-Preserving Perpetual Futures Exchange

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors