Skip to content

sevryn-labs/nocturne

Repository files navigation

pUSD Lending Protocol

Generic badge Generic badge Generic badge Generic badge

A privacy-preserving collateralised lending protocol built on the Midnight Network. Deposit tNight as collateral, mint pUSD synthetic stablecoins, and maintain positions privately via zero-knowledge proofs: individual debt and collateral amounts are never exposed on-chain.

Conceptually equivalent to MakerDAO/Liquity, built for Midnight's ZK-first architecture.


Recent Updates (v3)

  • Oracle Price Feed: All ratio checks now use oracle prices with 4-decimal precision ($1.00 = 10000). Admin-updateable via updateOraclePrice circuit.
  • Debt Ceiling: System-wide cap on total pUSD issuance (default: 10M). Prevents unbounded protocol exposure.
  • Governance Circuits: 8 admin-callable circuits for live parameter tuning: minting ratio, liquidation ratio, penalty, staleness limit, debt ceiling, minimum debt, and pause control.
  • Insurance Fund: On-chain Counter for protocol reserves. Anyone can contribute via fundInsurance(). Liquidation penalty fees route here.
  • Pause Mechanism: Emergency pause blocks risky operations (mint, withdraw, liquidate, transfer) while risk-reducing operations (deposit, repay) remain available.
  • Minimum Debt: Dust prevention: vaults must maintain a minimum debt position (default: 100 pUSD).
  • 100+ Tests: Test suite expanded from 48 to 100+ across 22 sections, including oracle stress tests and combined constraint enforcement.
  • Admin REST API: 6 new /api/admin/* endpoints for governance operations.

Table of Contents


How It Works

Deposit tNight → Mint pUSD (up to 66% of collateral value)
                       ↕
            Collateral ratio ≥ 150% (enforced by ZK proof)
                       ↕
     Repay pUSD → Withdraw tNight

Economic Rules

All rules are enforced entirely inside the Compact ZK circuit: no off-chain trust required:

Rule Constraint (v3 with oracle price)
Minimum collateral ratio collateral × price × 100 ≥ debt × ratio × 10000
Mint cap (existingDebt + mintAmount) × ratio × 10000 ≤ collateral × price × 100
Debt ceiling totalDebt + mintAmount ≤ debtCeiling
Minimum vault debt newDebt ≥ minDebt (default: 100 pUSD)
Withdraw floor remaining collateral must still satisfy ratio at oracle price
Liquidation guard position must have ratio < liquidationRatio at oracle price
Pause protection mint, withdraw, liquidate, transfer blocked when paused == 1

Oracle pricing: Price uses 4-decimal precision ($1.00 = 10000). Arithmetic note: Division is not supported in Compact, so all ratio checks are reformulated as multiplications.

Privacy Model

Data Visibility Reason
totalCollateral 🌐 Public Protocol solvency audit
totalDebt 🌐 Public Verify no unbacked pUSD (totalSupply == totalDebt)
totalSupply 🌐 Public The aggregate supply of the fungible pUSD token
liquidationRatio / mintingRatio 🌐 Public Transparent risk parameter
oraclePrice / oracleTimestamp 🌐 Public Current collateral price feed
debtCeiling / minDebt 🌐 Public System limits
insuranceFund 🌐 Public Protocol reserve balance
paused 🌐 Public Emergency pause state
Your collateral 🔒 Private Hidden in ZK: never on-chain
Your debt 🔒 Private Hidden in ZK: never on-chain
pUSD Balances 🌐 Public ERC20-style transparent balances on-chain
Your identity 🔒 Private Pseudonymous (Midnight public key only)

Private state lives in a user-controlled local LevelDB database and is supplied to ZK circuits via witness functions. The Compact runtime generates a zero-knowledge proof that constraints hold without revealing actual values.


Architecture

The protocol supports two interaction modes: a React Web UI and a terminal CLI: both backed by the same contract and wallet SDK:

┌──────────────────────┐    ┌─────────────────────────────────────┐
│  React Web UI        │    │           CLI (cli.ts)               │
│  (lending-ui)        │    │  Menu-driven terminal interface      │
│  localhost:5173      │    └──────────────────┬──────────────────┘
└──────────┬───────────┘                       │
           │ HTTP/JSON                         │ direct import
┌──────────▼───────────┐                       │
│  REST API Server     │                       │
│  (lending-api)       ├───────────────────────┤
│  localhost:3001      │                       │
│  Express + LendingService                    │
└──────────┬───────────┘                       │
           │                                   │
┌──────────▼───────────────────────────────────▼──────────────────┐
│                      TypeScript API (api.ts / lending-service)   │
│  Wallet construction · Provider setup · All lending + admin ops  │
│  Private state management (LevelDB read/write)                   │
└────┬──────────────┬──────────────┬──────────────┬───────────────┘
     │              │              │              │
┌────▼────┐  ┌──────▼─────┐  ┌────▼────┐  ┌─────▼──────┐
│ Wallet  │  │  midnight-  │  │  Proof  │  │  Indexer   │
│ Facade  │  │ js-contracts│  │  Server │  │  (public   │
│(3 subs) │  │(deploy/call)│  │  (ZK)   │  │   data)    │
└─────────┘  └──────┬──────┘  └─────────┘  └────────────┘
                    │
        ┌───────────▼───────────┐
        │  Compact ZK Contract  │
        │   (lending.compact)   │
        │                       │
        │  5 core circuits:     │
        │  · depositCollateral  │
        │  · mintPUSD           │
        │  · repayPUSD          │
        │  · withdrawCollateral │
        │  · liquidate          │
        │                       │
        │  8 admin circuits:    │
        │  · updateOraclePrice  │
        │  · updateMintingRatio │
        │  · updateLiquidation… │
        │  · updateDebtCeiling  │
        │  · updateStaleness…   │
        │  · updateMinDebt      │
        │  · updateLiqPenalty…  │
        │  · setPaused          │
        │                       │
        │  1 public circuit:    │
        │  · fundInsurance      │
        └───────────────────────┘

Note: All export ledger values (oracle price, debt ceiling, etc.) are readable directly via the Midnight indexer. No dedicated query circuits are needed.

The wallet is composed of three sub-wallets orchestrated by WalletFacade:

  • ShieldedWallet: ZK-shielded transactions (zswap)
  • UnshieldedWallet: transparent tNight transactions
  • DustWallet: dust fee token management

Project Structure

lending_protocol/
├── contract/                              # Compact smart contract
│   ├── package.json                       # @midnight-ntwrk/lending-contract
│   ├── src/
│   │   ├── lending.compact                # ← Core protocol (5 core + 9 admin + 5 token)
│   │   ├── witnesses.ts                   # Private state type + witness functions
│   │   ├── index.ts                       # Package entry point + Lending namespace
│   │   └── managed/lending/               # Generated by `npm run compact`
│   │       ├── contract/index.js          #   TypeScript bindings
│   │       ├── keys/                      #   ZK proving/verifying keys
│   │       └── zkir/                      #   Circuit IR files
│   └── src/test/
│       ├── lending-simulator.ts           # In-memory test harness (no network)
│       └── lending.test.ts                # 100+ unit tests (22 sections, vitest)
│
├── lending-api/                           # REST API server (NEW)
│   ├── package.json                       # @midnight-ntwrk/lending-api
│   └── src/
│       ├── server.ts                      # Express server (port 3001)
│       ├── lending-service.ts             # Core service: wallet, contract, operations
│       ├── config.ts                      # Network configs (mirrors lending-cli)
│       └── common-types.ts                # Shared types
│
├── lending-ui/                            # React web frontend (NEW)
│   ├── package.json                       # @midnight-ntwrk/lending-ui
│   ├── index.html                         # Entry HTML
│   ├── vite.config.ts                     # Vite + API proxy config
│   └── src/
│       ├── main.tsx                       # React entry point
│       ├── App.tsx                        # Root component + routing
│       ├── api.ts                         # Typed HTTP client for lending-api
│       ├── context.tsx                    # React Context for global state
│       ├── index.css                      # Design system (dark theme, glassmorphism)
│       └── pages/
│           ├── Setup.tsx                  # Wallet init + contract deploy/join
│           ├── Dashboard.tsx              # Public protocol state
│           ├── Position.tsx               # Private position + health indicator
│           └── Actions.tsx                # Deposit/Mint/Repay/Withdraw/Liquidate
│
├── lending-cli/                           # CLI & API layer (original)
│   ├── package.json                       # @midnight-ntwrk/lending-cli
│   ├── .env                               # Indexer container env vars
│   ├── standalone.yml                     # Docker Compose: full local stack
│   ├── proof-server.yml                   # Docker Compose: proof server only
│   └── src/
│       ├── api.ts                         # TypeScript API (all 5 operations + wallet)
│       ├── cli.ts                         # Interactive terminal CLI (menus + display)
│       ├── common-types.ts                # Shared types (providers, positions, circuits)
│       ├── config.ts                      # Network configs (Standalone/Preview/Preprod)
│       ├── logger-utils.ts                # Pino logger setup
│       ├── preprod.ts                     # Entry: npm run preprod
│       ├── preprod-start-proof-server.ts  # Entry: npm run preprod-ps (auto proof server)
│       ├── preview.ts                     # Entry: npm run preview
│       ├── preview-start-proof-server.ts  # Entry: npm run preview-ps (auto proof server)
│       └── standalone.ts                  # Entry: npm run standalone (full Docker stack)
│
├── PROTOCOL.md                            # Full protocol documentation
├── MIGRATION_GUIDE.md                     # midnight-js 2.x → 3.0.0 migration guide
├── package.json                           # Root workspace (npm workspaces)
└── README.md                              # This file

Prerequisites

  • Node.js v20+: node --version to check

    Node.js v22+ is recommended for full SDK compatibility. On v20, a few wallet SDK methods use Iterator Helpers (.entries().every(), Set.union(), .keys().toArray()) that require manual patching: see Troubleshooting.

  • Docker with docker compose: for the proof server and standalone mode

Compact Developer Tools

The Compact devtools manage the ZK contract compiler.

# Install the Compact devtools
curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh

# Add to PATH
source $HOME/.local/bin/env

# Install the toolchain version used by this project
compact update 0.28.0

# Verify
compact compile --version
# → 0.28.0

Already installed? Run compact self update to get the latest. Use compact clean to reset if you encounter issues.


Quick Start: Web UI

The recommended way to interact with the lending protocol is via the React Web UI.

1. Install dependencies

npm install

2. Build the smart contract

cd contract
npm run compact    # compile Compact → ZK circuits + TypeScript bindings
npm run build      # tsc compile
npm run test       # run 100+ unit tests (no network required)

Expected output from npm run compact:

  circuit "allowance"         (k=10, rows=634)
  circuit "approve"           (k=10, rows=711)
  circuit "balanceOf"         (k=9, rows=344)
  circuit "decimals"          (k=6, rows=26)
  circuit "depositCollateral" (k=9, rows=201)
  circuit "liquidate"         (k=10, rows=724)
  circuit "mintPUSD"          (k=10, rows=909)
  circuit "repayPUSD"         (k=10, rows=634)
  circuit "totalSupply"       (k=6, rows=26)
  circuit "transfer"          (k=10, rows=1013)
  circuit "transferFrom"      (k=11, rows=1482)
  circuit "withdrawCollateral"(k=9, rows=354)

The first run downloads zero-knowledge parameters (~500 MB). This is a one-time download cached at ~/.compact/.

3. Start the Proof Server

cd lending-cli && docker compose -f proof-server.yml up -d

Wait until it shows listening on 0.0.0.0:6300 in the logs.

4. Start the API Server

# From the project root
npm run dev:api

The REST API server starts on http://localhost:3001. You should see:

[lending-api] Network: preprod | Port: 3001
[lending-api] Server running at http://localhost:3001

5. Start the Web Frontend

In a second terminal:

# From the project root
npm run dev:ui

Open http://localhost:5173 in your browser. The Vite dev server proxies API requests to the backend automatically.

6. Use the Protocol

  1. Setup page → Click "Create New Wallet" (or restore from seed)
  2. Fund your wallet with tNight from the Preprod Faucet
  3. Setup page → Click "Deploy New Contract" (or join an existing one)
  4. Dashboard → View public protocol state
  5. My Position → View your private collateral and debt
  6. Actions → Deposit, Mint, Repay, Withdraw, or Liquidate

⏱ Wallet initialization takes 30–60 seconds (sync + DUST registration). Contract deployment takes 1–3 minutes (ZK proof generation).


Quick Start: CLI

The original terminal CLI is still fully functional.

Option A: Preprod with auto-start proof server (recommended for first run):

cd lending-cli
npm run preprod-ps

This pulls the proof server Docker image, starts it, then launches the CLI connected to the public Preprod testnet.

Option B: Preprod with manual proof server:

# Terminal 1: proof server
cd lending-cli && docker compose -f proof-server.yml up

# Terminal 2: CLI (once proof server shows "listening on 0.0.0.0:6300")
cd lending-cli && npm run preprod

Option C: Fully local standalone (no testnet needed):

cd lending-cli
npm run standalone

Starts a complete local Midnight stack via Docker Compose (node, indexer, proof server) with a pre-funded genesis wallet: no faucet needed.


Using the Lending Protocol CLI

Step 1: Create or restore a wallet

╔══════════════════════════════════════════════════════════════════╗
║                                                                  ║
║    pUSD Privacy-Preserving Lending Protocol                      ║
║    ─────────────────────────────────────                         ║
║    Built on Midnight Network · Powered by ZK Proofs              ║
║                                                                  ║
╚══════════════════════════════════════════════════════════════════╝

  Wallet Setup
  ────────────────────────────────────────────────────────────
  [1] Create a new wallet (random seed)
  [2] Restore wallet from existing seed
  [3] Exit
  • Choose [1] for a fresh wallet: the CLI prints your seed and unshielded address.
  • Choose [2] to restore an existing wallet with its 64-hex seed.
  • In standalone mode, the genesis wallet is loaded automatically (no prompt).

Always save your seed. It's the only way to restore your wallet and your private position data.

Step 2: Fund your wallet with tNight

  1. Copy the unshielded address (mn_addr_preprod1...) displayed by the CLI.
  2. Visit the Preprod Faucet.
  3. Paste your address and request tNight.
  4. The CLI detects incoming funds automatically.

In standalone mode, the genesis wallet is pre-funded: skip this step.

Step 3: Wait for DUST

After receiving tNight, the CLI registers your NIGHT UTXOs for DUST generation. DUST is the non-transferable fee resource required for all Midnight transactions.

  ✓ Registering 1 NIGHT UTXO(s) for dust generation
  ✓ Waiting for dust tokens to generate
  ✓ Configuring providers

Step 4: Deploy or join a contract

  Contract Setup                         DUST: 405,083,000,000,000
  ────────────────────────────────────────────────────────────────
  [1] Deploy a new pUSD Lending contract
  [2] Join an existing pUSD Lending contract
  [3] Monitor DUST balance
  [4] View Coin Public Key (for receiving pUSD)
  [5] Exit
  • [1] Deploy: creates a new lending protocol on-chain. Save the contract address shown on success.
  • [2] Join: enter a contract address to interact with an existing deployment.
  • [3] Monitor: live-stream your DUST balance (useful while waiting for generation).
  • [4] View Coin Public Key: display your ZSwap shielded public key.

Tip: You can run multiple CLI tabs with different seeds to simulate multi-user behavior. The LevelDB state is isolated per wallet, preventing any cross-wallet AES encryption conflicts.

Step 5: Lend and borrow

  Lending Actions                        DUST: 405,083,000,000,000
  ────────────────────────────────────────────────────────────────
  [1] Deposit Collateral (tNight → protocol)
  [2] Mint pUSD            (borrow against collateral)
  [3] Repay pUSD           (reduce debt)
  [4] Withdraw Collateral  (reclaim tNight)
  [5] Liquidate            (claim undercollateralised position)
  [6] View Protocol State  (public totals)
  [7] View My Position     (private balance)
  [8] View Wallet Balances (tNight & pUSD)
  [9] View Coin Public Key (for receiving pUSD)
  [10] Transfer pUSD       (send tokens to another coin public key)
  [11] Exit

Example flow:

[1] Deposit  3000 tNight   → collateral = 3000, debt = 0
[2] Mint     2000 pUSD     → collateral = 3000, debt = 2000  (ratio = 150% ✓)
[3] Repay    2000 pUSD     → collateral = 3000, debt = 0
[4] Withdraw 3000 tNight   → collateral = 0,    debt = 0  ✓

View My Position shows your private state (stored locally, never on-chain):

  My Private Position
  ──────────────────────────────────────────────────────────────
  Collateral (tNight) : 3,000
  Debt (pUSD)         : 2,000
  Collateral Ratio    : 150%
  Health              : ✓ Healthy (ratio ≥ 150%)

View Protocol State shows the public on-chain totals:

  Public Protocol State
  ──────────────────────────────────────────────────────────────
  Total Collateral (tNight) : 3,000
  Total pUSD Debt           : 2,000
  Liquidation Ratio         : 150%
  Minting Ratio             : 150%

Collateral Ratio Quick Reference

With oracle price at $1.00 (10000):

Collateral Max Mintable pUSD Ratio
1,500 1,000 150% (minimum)
3,000 2,000 150%
10,000 6,666 150%

Formula: maxDebt = (collateral × oraclePrice × 100) / (mintingRatio × 10000)

With oracle price at $2.00 (20000):

Collateral Max Mintable pUSD Ratio
750 1,000 150%
1,500 2,000 150%
5,000 6,666 150%

Web Frontend Features

The React UI (lending-ui/) provides four pages:

Page Route Description
Setup /setup Wallet initialization (new or restore), contract deploy/join
Dashboard /dashboard Public protocol state: total collateral, total debt, ratios, utilization
My Position /position Private state with health indicator (Green ≥170% / Yellow 150–170% / Red <150%)
Actions /actions Tabbed forms for Deposit, Mint, Repay, Withdraw, Liquidate

UX Highlights

  • Live Ratio Preview: When typing amounts in mint/withdraw/deposit/repay forms, the new collateral ratio is calculated and displayed instantly with color-coded health warnings
  • Health Indicator: Color-coded badge (🛡 Healthy / ⚠️ At Risk / 🚨 Critical) with animated ratio bar and liquidation threshold markers
  • ZK Proof Feedback: Loading states with estimated timing for proof generation
  • Error Classification: Errors are categorized (WALLET_NOT_INITIALIZED, INSUFFICIENT_BALANCE, PROOF_SERVER_DOWN, etc.) and displayed with user-friendly messages
  • Auto-Refresh: Protocol state refreshes every 30 seconds; position refreshes after every action
  • Premium Dark Theme: Glassmorphism, gradient accents, Inter + JetBrains Mono typography, micro-animations

API Endpoints

The REST API server (lending-api/) exposes the following endpoints on http://localhost:3001:

Wallet

Method Endpoint Body Description
POST /api/wallet/initialize { seed?: string } Create or restore wallet
GET /api/wallet/info : Get wallet balances and addresses
POST /api/wallet/wait-for-funds : Block until tNight balance > 0
POST /api/wallet/register-dust : Register for DUST generation
GET /api/wallet/dust : Get DUST balance

Contract

Method Endpoint Body Description
POST /api/contract/deploy : Deploy a new lending contract
POST /api/contract/join { address: string } Join existing contract

State

Method Endpoint Description
GET /api/protocol/state Public protocol state (totals, ratios, oracle, ceiling, insurance, pause)
GET /api/position Private position (collateral, debt, oracle-adjusted ratio)
GET /api/health Server health + connection status

Lending Actions

Method Endpoint Body Description
POST /api/actions/deposit { amount: string } Deposit tNight collateral
POST /api/actions/mint { amount: string } Mint pUSD
POST /api/actions/repay { amount: string } Repay pUSD debt
POST /api/actions/withdraw { amount: string } Withdraw tNight
POST /api/actions/liquidate { victimCollateral, victimDebt } Liquidate undercollateralised position
POST /api/actions/transfer { recipientPublicKey, amount } Transfer pUSD to another wallet

Admin / Governance (v3)

Method Endpoint Body Description
POST /api/admin/oracle-price { price, blockHeight } Update oracle price (4-decimal: $1.00 = 10000)
POST /api/admin/minting-ratio { ratio } Update minting ratio (110–300%)
POST /api/admin/liquidation-ratio { ratio } Update liquidation ratio (110–300%)
POST /api/admin/debt-ceiling { ceiling } Update system debt ceiling
POST /api/admin/pause { state: 0|1 } Pause (1) or unpause (0) the protocol
POST /api/admin/fund-insurance { amount } Contribute to insurance fund

All responses use JSON. BigInt values are serialized as strings. Errors include an errorType field for frontend categorization.


Running Tests

cd contract
npm test
 ✓ src/test/lending.test.ts (100+ tests)
   ✓ 1.  Contract Initialization (4)
   ✓ 2.  Collateral Management (depositCollateral) (6)
   ✓ 3.  Minting Tests (mintPUSD) (6)
   ✓ 4.  Repayment Tests (repayPUSD) (6)
   ✓ 5.  Withdraw Collateral Tests (withdrawCollateral) (6)
   ✓ 6.  Liquidation Tests (liquidate) (5)
   ✓ 7.  Token Transfer Tests (transfer) (6)
   ✓ 8.  Allowance + Approval Tests (approve) (4)
   ✓ 9.  transferFrom Tests (transferFrom) (5)
   ✓ 10. Multi-user System Tests (1)
   ✓ 11. Protocol Invariant Tests (1)
   ✓ 12. Sequential Workflow Tests (2)
   ✓ 13. Boundary Tests (1)
   ✓ 14. Randomized / Fuzz Tests (1)
   ── v3 New Sections ──────────────────────
   ✓ 15. Oracle Price Tests (5)
   ✓ 16. Debt Ceiling Tests (4)
   ✓ 17. Minimum Debt Tests (3)
   ✓ 18. Pause Mechanism Tests (9)
   ✓ 19. Insurance Fund Tests (4)
   ✓ 20. Governance Parameter Tests (8)
   ✓ 21. Oracle-Integrated Stress Tests (3)
   ✓ 22. Combined System Integrity Tests (2)

Tests run against the LendingSimulator: an in-memory harness that wraps the Compact-generated contract. No Midnight node, no proof server, no Docker needed.


Standalone Mode (fully local)

cd lending-cli
npm run standalone

Starts a complete local Midnight stack via Docker Compose:

Container Image Purpose
lending-node midnightntwrk/midnight-node:0.20.0 Local Midnight node
lending-indexer midnightntwrk/indexer-standalone:3.0.0 Block indexer + GraphQL API
lending-proof-server midnightntwrk/proof-server:7.0.0 ZK proof generation

Uses the genesis seed (0...01) with pre-minted tNight: no faucet needed.


Network Targets

Network Description Command Proof Server
Standalone Fully local (Docker) npm run standalone Included in Docker stack
Preview Public preview testnet npm run preview-ps Auto-started via Docker
Preprod Public preprod testnet npm run preprod-ps Auto-started via Docker

For manual proof server management, use the -ps-less variants (preview, preprod) and start the proof server separately:

cd lending-cli && docker compose -f proof-server.yml up -d

Environment Variables

Variable Default Used By Description
MIDNIGHT_NETWORK preprod lending-api Network to connect to: standalone, preview, or preprod
PORT 3001 lending-api HTTP port for the REST API server

Example:

MIDNIGHT_NETWORK=standalone PORT=4000 npm run dev:api

Root Workspace Scripts

Script Description
npm run dev:api Start the REST API server (port 3001)
npm run dev:ui Start the React dev server (port 5173)
npm run dev:cli Start the CLI in standalone mode

Troubleshooting

Issue Solution
compact: command not found Run source $HOME/.local/bin/env to add it to your PATH
connect ECONNREFUSED 127.0.0.1:6300 Start the proof server: cd lending-cli && docker compose -f proof-server.yml up
imbalances.fallible.entries(...).every is not a function Node.js v20 compatibility issue. The wallet SDK uses Iterator Helpers (Node 22+). Patch node_modules/@midnight-ntwrk/wallet-sdk-shielded/dist/v1/TransactionImbalances.js: wrap .entries().every() calls with Array.from(). Also patch Imbalances.js (Set.union() → spread syntax) and TransactionOps.js (.keys().toArray()Array.from()). Or upgrade to Node.js v22+.
Insufficient collateral: ratio below minting threshold Your collateral ratio would drop below 150% after minting. Deposit more collateral first. Formula: need collateral ≥ (currentDebt + mintAmount) × 1.5
Withdrawal would breach liquidation ratio Withdrawing would push your ratio below 150%. Repay some debt first.
Proof server hangs on Mac ARM (Apple Silicon) Docker Desktop → Settings → General → "Virtual Machine Options" → select Docker VMM. Restart Docker.
Failed to clone intent during deploy Wallet SDK signing bug: already worked around in this codebase (see signTransactionIntents in api.ts)
DUST balance drops to 0 after failed tx Known wallet SDK 1.0.0 issue. Restart the CLI to release locked DUST coins
Wallet shows 0 balance after faucet Wait for sync to complete. Confirm you used the correct unshielded address (mn_addr_...)
Cannot find module './managed/lending/contract/index.js' Contract not compiled yet: run cd contract && npm run compact first
Tests fail with "Cannot find module" Build the contract first: cd contract && npm run compact && npm run build
Node.js warnings about experimental features Normal: --loader ts-node/esm triggers these; they don't affect functionality

Key Dependencies

Package Version Purpose
@midnight-ntwrk/compact-runtime 0.14.0 Compact contract runtime
@midnight-ntwrk/midnight-js-contracts 3.0.0 Contract deploy/call/join
@midnight-ntwrk/wallet-sdk-facade 1.0.0 Unified wallet interface
@midnight-ntwrk/wallet-sdk-hd 3.0.0 HD key derivation
@midnight-ntwrk/ledger-v7 (via @midnight-ntwrk/ledger) ^4.0.0 Ledger types & primitives
express ^5.1.0 REST API framework (lending-api)
react ^19.0.0 UI framework (lending-ui)
react-router-dom ^7.1.0 Client-side routing
vite ^6.0.0 Frontend build tool
tsx ^4.21.0 TypeScript execution (Node v24 compatible)
pino ^10.3.1 Structured logging
testcontainers ^11.12.0 Docker management for standalone mode

Roadmap

v3 delivers a production-grade foundation. Remaining items for full mainnet readiness:

  • Oracle Price Feed: ✅ Implemented in v3: oraclePrice with 4-decimal precision, updateOraclePrice admin circuit, staleness protection.
  • Governance Circuits: ✅ Implemented in v3: 8 admin circuits for live parameter tuning.
  • Debt Ceiling & Min Debt: ✅ Implemented in v3: system-wide debt cap and dust prevention.
  • Insurance Fund: ✅ Implemented in v3: on-chain protocol reserve.
  • Pause Mechanism: ✅ Implemented in v3: emergency circuit with selective operation blocking.
  • On-chain Admin Access Control: Phase 2: verify caller == admin key in governance circuits (currently enforced at API layer).
  • Multi-sig Governance: Phase 2: N-of-M key holder approval for parameter changes.
  • Timelock: Phase 3: 48h delay on parameter changes before activation.
  • Decentralized Oracles: Phase 3: integrate DIA/Chainlink via ZK-bridged price feeds.
  • Redemption Mechanism: Peg stability: allow pUSD holders to redeem at $1 from riskiest vaults.
  • Stability Fee: Dynamic interest rate adjustable via governance to maintain the $1 peg.
  • Keeper Bot Reference: Reference implementation for automated liquidation monitoring.
  • Flash Loans: Atomic ZK-safe flash borrowing of pUSD.

Further Reading


License

Licensed under the Apache License 2.0.

About

privacy-preserving collateralised lending protocol

Topics

Resources

License

Stars

Watchers

Forks

Contributors