{"id":47301314,"url":"https://github.com/sevryn-labs/midnight-escrow","last_synced_at":"2026-03-31T07:00:44.161Z","repository":{"id":341896082,"uuid":"1159927046","full_name":"sevryn-labs/midnight-escrow","owner":"sevryn-labs","description":"A privacy-preserving escrow contract built on the Midnight Network","archived":false,"fork":false,"pushed_at":"2026-03-04T19:26:08.000Z","size":819,"stargazers_count":3,"open_issues_count":9,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-13T03:14:03.792Z","etag":null,"topics":["compact","midnightntwrk"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sevryn-labs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-17T10:34:39.000Z","updated_at":"2026-03-12T15:42:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sevryn-labs/midnight-escrow","commit_stats":null,"previous_names":["tusharpamnani/midnight-escrow","sevryn-labs/midnight-escrow"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/sevryn-labs/midnight-escrow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevryn-labs%2Fmidnight-escrow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevryn-labs%2Fmidnight-escrow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevryn-labs%2Fmidnight-escrow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevryn-labs%2Fmidnight-escrow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sevryn-labs","download_url":"https://codeload.github.com/sevryn-labs/midnight-escrow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevryn-labs%2Fmidnight-escrow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31224248,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-31T04:08:55.938Z","status":"ssl_error","status_checked_at":"2026-03-31T04:08:47.883Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compact","midnightntwrk"],"created_at":"2026-03-17T03:00:22.710Z","updated_at":"2026-03-31T07:00:44.155Z","avatar_url":"https://github.com/sevryn-labs.png","language":"TypeScript","funding_links":[],"categories":["Finance \u0026 DeFi"],"sub_categories":[],"readme":"# Midnight Escrow Contract\n\n[![Compact Runtime](https://img.shields.io/badge/compact--runtime-0.14.0-1abc9c.svg)](https://shields.io/) [![midnight-js](https://img.shields.io/badge/midnight--js-3.0.0-blueviolet.svg)](https://shields.io/) [![wallet-sdk](https://img.shields.io/badge/wallet--sdk-1.0.0-blue.svg)](https://shields.io/) [![Tests](https://img.shields.io/badge/Tests-50%20passing-brightgreen.svg)](https://shields.io/)\n\nA **privacy-preserving escrow contract built on the Midnight Network**. A buyer deposits funds which are released to a seller only when the seller proves knowledge of a secret agreed upon at escrow creation — verified entirely by a Zero-Knowledge Proof, with no trusted intermediary.\n\nThe project serves as a **reference implementation** for developers building privacy-preserving financial primitives on Midnight.\n\n---\n\n# Overview\n\nTraditional escrow systems require a trusted third party. This implementation replaces that trust with cryptography.\n\nThe contract enforces the following rules in ZK:\n\n* Only the key derived from the buyer's secret key can create or refund an escrow\n* Only the key derived from the seller's secret key can accept or release an escrow\n* Release is only permitted when the seller can prove they hold the correct `(releaseSecret, nonce, amount)` triple that matches the commitment stored on-chain at creation time\n\nNo secret values are ever written to the public ledger. All verification happens inside ZK circuits compiled from the Compact contract.\n\n---\n\n# Demo\n\n▶️ **YouTube Demo:**\n*(COMING SOON)*\n\nThe demo shows:\n\n1. Contract deployment on Midnight Preprod\n2. Escrow creation by buyer (Bob)\n3. Escrow acceptance by seller (Alice)\n4. Secret-based conditional release\n5. Funds claimed by Alice\n\n---\n\n# Features\n\n### Zero-Knowledge Release Verification\n\nAt creation the buyer commits to `persistentCommit([amount_bytes || hash(releaseSecret)], nonce)`. At release the seller must reproduce this commitment exactly, proving they have the correct secret and nonce — without revealing either value on the ledger.\n\n### Derived Identity Keys\n\nParticipant identities (`buyer`, `seller` ledger fields) are **derived public keys**: `persistentHash([\"midnight:escrow:key\", secretKey])`. Raw secret keys are never disclosed on-chain.\n\n### Privacy-Preserving State Machine\n\nThe contract enforces a strict four-state machine:\n\n```\nEMPTY → FUNDED → RELEASED\n                → REFUNDED\n```\n\nEvery circuit asserts the current state before acting, preventing double-spend and invalid transitions.\n\n### Interactive CLI\n\nA full-featured CLI supports all escrow operations with an interactive menu:\n\n* Deploy new escrow contract\n* Join existing escrow contract by address\n* Create escrow (buyer)\n* Accept escrow (seller)\n* Release funds with secret proof (seller)\n* Refund (buyer)\n* Monitor DUST balance\n* Show escrow identity (encryption public key)\n\n### Isolated Private State Per Run\n\nEach CLI session uses a randomly suffixed LevelDB store name (`escrow-private-state-\u003crandom\u003e`), preventing decryption failures when switching wallet seeds between runs.\n\n---\n\n# Project Structure\n\n```\n.\n├── contract/                          # Midnight Compact smart contract\n│   ├── src/\n│   │   ├── escrow.compact             # Contract logic (circuits, state machine)\n│   │   ├── witnesses.ts               # ZK witness definitions (EscrowPrivateState)\n│   │   ├── index.ts                   # Package entry point\n│   │   └── test/\n│   │       ├── escrow-simulator.ts    # In-process contract simulator\n│   │       └── escrow.test.ts         # 50 unit tests (Vitest)\n│   │   └── managed/escrow/            # Compact compiler output (generated)\n│   ├── package.json\n│   └── tsconfig.json\n│\n├── counter-cli/                       # CLI client\n│   ├── src/\n│   │   ├── api.ts                     # Wallet, provider setup, contract calls\n│   │   ├── cli.ts                     # Interactive menu and user flows\n│   │   ├── config.ts                  # Network configs (Preprod, Preview, Standalone)\n│   │   ├── common-types.ts            # Shared TypeScript types\n│   │   ├── logger-utils.ts            # Pino logger (file + pretty stdout)\n│   │   ├── preprod.ts                 # Preprod entry point\n│   │   ├── preprod-start-proof-server.ts\n│   │   ├── preview.ts                 # Preview network entry point\n│   │   ├── preview-start-proof-server.ts\n│   │   └── standalone.ts              # Standalone / local entry point\n│   ├── proof-server.yml               # Docker Compose: proof server only\n│   ├── standalone.yml                 # Docker Compose: full local stack\n│   └── package.json\n│\n├── package.json                       # npm workspace root\n├── package-lock.json\n└── README.md\n```\n\n---\n\n# Architecture\n\n## Components\n\n### 1. Compact Smart Contract (`contract/src/escrow.compact`)\n\nWritten in Midnight's **Compact language**. Defines:\n\n* **Ledger state**: `buyer`, `seller` (derived keys), `termsCommitment`, `state` (enum), `round` (counter)\n* **Witnesses**: `secretKey`, `releaseSecret`, `nonce`, `escrowAmount` (supplied privately per circuit call)\n* **Circuits**: `createEscrow`, `acceptEscrow`, `release`, `refund`, `getReleaseHash`\n\n### 2. Witness Layer (`contract/src/witnesses.ts`)\n\nDefines `EscrowPrivateState`:\n\n```typescript\ntype EscrowPrivateState = {\n  secretKey: Uint8Array;      // 32 bytes — identity key\n  releaseSecret: Uint8Array;  // 32 bytes — pre-image of the commitment\n  nonce: Uint8Array;          // 32 bytes — commitment randomness\n  amount: bigint;             // escrow value\n}\n```\n\nWitness functions feed private state into ZK circuits at proof time.\n\n### 3. CLI Client (`counter-cli/src/`)\n\n* **`api.ts`** — builds the wallet (HD keys → shielded + unshielded + dust sub-wallets), configures providers, and wraps each contract circuit call\n* **`cli.ts`** — interactive readline menu\n* **`config.ts`** — `PreprodConfig`, `PreviewConfig`, `StandaloneConfig` for different networks\n\n### 4. Proof Server\n\nA local Docker service (`midnightntwrk/proof-server:7.0.0`) that generates ZK proofs. Runs at `http://127.0.0.1:6300`.\n\n### 5. Midnight Network (Preprod / Preview)\n\nVerifies ZK proofs and stores contract state on-chain. The CLI connects via indexer GraphQL and an RPC node.\n\n---\n\n## Architecture Diagram\n\n![architecture](image.png)\n\n---\n\n## Contract State Machine\n\n```\n             createEscrow(sellerPk, amount)\n  [EMPTY] ──────────────────────────────────► [FUNDED]\n                                                  │\n                              ┌───────────────────┴───────────────────┐\n                              │                                       │\n                    release()  │                             refund()  │\n                  (seller + ZK proof)                     (buyer only) │\n                              ▼                                       ▼\n                          [RELEASED]                            [REFUNDED]\n```\n\nAll transitions assert the current state. Invalid transitions (e.g. release on REFUNDED, double-release) throw a circuit assertion error.\n\n---\n\n## ZK Commitment Scheme\n\nOn `createEscrow`, the buyer stores:\n\n```\ntermsCommitment = persistentCommit([amount_as_bytes32 || hash(releaseSecret)], nonce)\n```\n\nOn `release`, the seller recomputes:\n\n```\nrecomputed = persistentCommit([amount_as_bytes32 || hash(releaseSecret)], nonce)\nassert(recomputed == termsCommitment)\n```\n\nThis is fully verified inside the ZK circuit; the seller's `releaseSecret` and `nonce` are private witness inputs that never appear on the public ledger.\n\n---\n\n# Getting Started\n\n## Prerequisites\n\n* Node.js (v18+)\n* Docker (for the Proof Server)\n* Access to the Midnight Preprod faucet\n\n\u003e ⚠️ **Important**: Each CLI run creates a fresh isolated LevelDB private state store. Run only **one CLI session at a time** to avoid port/resource conflicts.\n\n---\n\n## Installation\n\n### 1. Clone and install\n\n```bash\ngit clone https://github.com/tusharpamnani/midnight-escrow.git\ncd midnight-escrow\nnpm install\n```\n\n### 2. Compile the contract\n\n```bash\ncd contract\nnpm run compact\n```\n\nThis generates `contract/src/managed/escrow/` from `escrow.compact`.\n\n### 3. Build\n\n```bash\ncd contract \u0026\u0026 npm run build\ncd ../counter-cli \u0026\u0026 npm run build\n```\n\n### 4. Run tests\n\n```bash\ncd contract\nnpm run test\n```\n\nExpected output: **50 tests passing** across 6 test groups.\n\n---\n\n# Running the CLI\n\n## Step 1 — Start the Proof Server (Terminal 1)\n\n```bash\ncd counter-cli\ndocker compose -f proof-server.yml up\n```\n\nWait for:\n\n```\nActix runtime found; starting in Actix runtime\n```\n\nKeep this terminal running throughout the session.\n\n## Step 2 — Run the CLI (Terminal 2)\n\n```bash\ncd counter-cli\nnpm run preprod\n```\n\nThe CLI will prompt you to create or restore a wallet, then show the interactive escrow menu.\n\n\u003e **Alternative networks:**\n\u003e ```bash\n\u003e npm run preview     # Midnight Preview network\n\u003e npm run standalone  # Local Docker stack (requires standalone.yml)\n\u003e ```\n\n---\n\n# Test Data for Alice \u0026 Bob\n\n\u003e ⚠️ **For testing only. Never use these seeds on mainnet.**\n\n## Alice — Seller / Deployer\n\n```\nseed: 57bb166cb6bbf3a6cb5e93a26043e3e2d3c830b63b85286fe97619456a2a23f2\n```\n\nRole: deploys the contract, provides escrow identity, accepts and releases funds.\n\n## Bob — Buyer / Funder\n\n```\nseed: 2b477c42d95b5eb49222b25f9e5267c44cb15bef9646f086248bff24f43e727f\n```\n\nRole: funds the escrow, defines the release secret.\n\n```\ntest release secret: 4f8c2a9d7b1e3c5a8d6f2e9a1c4b7d8e5f3a9c2d6b1e4f8a7c9d2e5b6a1f3c4a\n```\n\n---\n\n# Usage Flow\n\n## Phase 1 — Deploy (Alice)\n\n```\nnpm run preprod\n```\n\n1. Choose **[2] Restore wallet from seed** → enter Alice's seed\n2. Wait for wallet sync and DUST generation\n3. Choose **[1] Deploy new escrow contract** — copy the printed contract address\n4. Choose **[6] Show My Escrow Identity** — copy the printed encryption public key\n5. Exit with **[7] Disconnect**\n\n---\n\n## Phase 2 — Fund Escrow (Bob)\n\n```\nnpm run preprod\n```\n\n1. Choose **[2] Restore wallet from seed** → enter Bob's seed\n2. Wait for wallet sync and DUST generation\n3. Choose **[2] Join existing escrow contract** → paste Alice's contract address\n4. Choose **[1] Create Escrow (Buyer)**:\n   - Paste Alice's **escrow public key** (encryption key from Phase 1)\n   - Enter an amount\n   - Enter the test release secret (64 hex chars)\n5. The CLI prints a **NONCE** — share both the nonce and secret with Alice\n6. Exit with **[7] Disconnect**\n\n---\n\n## Phase 3 — Release Funds (Alice)\n\n```\nnpm run preprod\n```\n\n1. Choose **[2] Restore wallet from seed** → enter Alice's seed\n2. Choose **[2] Join existing escrow contract** → paste the contract address\n3. Choose **[2] Accept Escrow (Seller)** — verifies Alice is the designated seller\n4. Choose **[3] Release Funds (Seller)**:\n   - Enter the amount, secret, and nonce Bob provided\n5. The ZK proof is generated and submitted — funds are released to Alice\n\n---\n\n## Refund Path (Bob)\n\nIf Alice never accepts, Bob can reclaim the escrow:\n\n```\n[4] Refund\n```\n\n---\n\n# Testing\n\nThe contract has a comprehensive Vitest test suite with **50 unit tests** covering all contract behaviour without requiring a network or proof server.\n\n```bash\ncd contract \u0026\u0026 npm run test\n```\n\n| Group | Tests | Coverage |\n|---|---|---|\n| **deployment** | 4 | Initial state, field defaults, determinism |\n| **escrow lifecycle** | 8 | All state transitions, ledger field correctness, full end-to-end |\n| **access control** | 6 | Buyer/seller/third-party role enforcement |\n| **secret \u0026 nonce validation** | 6 | Wrong secret, wrong nonce, wrong amount, `getReleaseHash` |\n| **invalid operations** | 10 | Double-release, double-refund, cross-state calls, re-creation |\n| **edge cases** | 8 | Zero amount, u64 max, commitment uniqueness, key derivation |\n| **multi-actor simulation** | 8 | Full Alice \u0026 Bob protocol, impersonation, Charlie takeover attempts |\n\n---\n\n# Troubleshooting\n\n### `Unsupported state or unable to authenticate data` on join\n\nThis means the local LevelDB store was encrypted with a different wallet seed. This is handled automatically — each CLI run uses a fresh store. If you still see this error, delete any `midnight-level-db` or `midnight-db-*` directories in `counter-cli/` and restart.\n\n### `Failed to join contract`\n\nEnsure the previous CLI session has fully exited before starting a new one.\n\n### `Only seller can accept`\n\nBob used Alice's **wallet address** instead of her **escrow identity key**. Use option **[6] Show My Escrow Identity** in Alice's session to get the correct encryption public key.\n\n### `Invalid release proof`\n\nThe secret, nonce, or amount does not match the commitment stored at creation. Verify Bob shared the correct values.\n\n### Proof server not reachable\n\nEnsure `docker compose -f proof-server.yml up` is running and you see the `Actix runtime found` message before starting the CLI.\n\n### Wallet has no DUST\n\nDUST is generated from `tNight` (unshielded) tokens. Fund the wallet from the [Midnight Preprod faucet](https://faucet.preprod.midnight.network/) and wait for DUST generation to complete before interacting with contracts.\n\n---\n\n# Future Improvements\n\nPossible extensions include:\n\n* timeout-based escrow refunds\n* multi-party escrow agreements\n* private dispute resolution\n* integration with privacy-preserving DeFi primitives\n* confidential liquidity pools and AMMs\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsevryn-labs%2Fmidnight-escrow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsevryn-labs%2Fmidnight-escrow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsevryn-labs%2Fmidnight-escrow/lists"}