Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Merkle — Sui Private Transfer Protocol

A privacy-preserving token transfer protocol on Sui, inspired by Tornado Cash and Mixoor. Users deposit SUI into a shared pool and withdraw to any address with zero on-chain link between sender and receiver.

Zero-knowledge proofs are generated entirely in the browser — the server never sees your secret or nullifier.


How It Works

  1. Deposit — User generates a random secret + nullifier in the browser, computes a Poseidon commitment, and deposits SUI into the on-chain pool. The commitment is stored in a Merkle tree.
  2. Wait — The backend indexer picks up the deposit event and updates its local Merkle tree.
  3. Prove — The browser fetches the Merkle path for the deposit and generates a Groth16 ZK proof entirely locally (using snarkjs + WASM). Secrets never leave the device.
  4. Withdraw — The proof is submitted to the relayer backend. The relayer verifies the proof on-chain and transfers SUI to any specified recipient.

Project Structure

Merkle/
├── circuits/           # Circom ZK circuit (transaction.circom)
├── contract/           # Sui Move smart contracts
│   └── sources/
│       ├── pool.move       # Pool state and Merkle root management
│       ├── deposit.move    # Deposit logic + event emission
│       ├── withdraw.move   # ZK proof verification + withdrawal
│       ├── verifier.move   # On-chain Groth16 verifier (BN254)
│       ├── admin.move      # Pool admin controls
│       ├── events.move     # Event type definitions
│       └── errors.move     # Error codes
├── backend-nodejs/     # Express.js relayer + Merkle tree indexer
│   └── src/
│       ├── indexer.ts      # Polls Sui events, builds Merkle tree
│       ├── merkle.ts       # In-memory Poseidon Merkle tree
│       ├── relayer.ts      # Signs and submits withdrawal TXs
│       ├── api.ts          # REST API routes
│       └── config.ts       # Environment configuration
└── client/             # Next.js frontend
    └── lib/
        ├── prover.ts       # Browser-side Groth16 proof generation
        ├── poseidon.ts     # Browser-side Poseidon hashing
        ├── converter.ts    # Arkworks G1/G2 compression for Sui
        ├── transactions.ts # Deposit/withdraw TX builders
        └── api.ts          # Backend API client

Prerequisites

  • Sui CLI (sui)
  • Node.js v20+
  • pnpm (backend) and bun (frontend)
  • A funded Sui testnet wallet for the relayer

Quick Start

1. Deploy the Contract

cd contract
sui client publish --gas-budget 200000000

Copy the PACKAGE_ID and POOL_OBJECT_ID from the output.

2. Start the Backend

cd backend-nodejs
cp .env.example .env   # fill in PACKAGE_ID, POOL_OBJECT_ID, RELAYER_SECRET_KEY
pnpm install
pnpm dev

Backend runs on http://localhost:3001.

3. Start the Frontend

cd client
cp .env.example .env   # set NEXT_PUBLIC_BACKEND_URL
bun install
bun dev

Frontend runs on http://localhost:3000.


Environment Variables

Backend (backend-nodejs/.env)

Variable Description
PORT Server port (default: 3001)
SUI_RPC_URL Sui RPC endpoint
PACKAGE_ID Deployed contract package ID
POOL_OBJECT_ID Pool shared object ID
RELAYER_SECRET_KEY Relayer Sui private key (suiprivkey1...)
POLL_INTERVAL_MS Indexer poll interval in ms (default: 3000)

Frontend (client/.env)

Variable Description
NEXT_PUBLIC_BACKEND_URL Backend API URL

API Reference

Method Endpoint Description
GET /api/health Health check + leaf count
GET /api/pool/stats Pool stats (root, leaf count, etc.)
GET /api/pool/root Current Merkle root
GET /api/pool/path/:index Merkle path for a leaf index
GET /api/relayer/status Relayer address + SUI balance
POST /api/relay/submit Submit a pre-proven withdrawal

Privacy Model

  • Secrets (secret, nullifier) are generated and stored only in the user's browser
  • The backend never receives secrets — only the ZK proof bytes
  • The nullifier hash prevents double-spending without revealing which deposit is being withdrawn
  • The Merkle tree root links the proof to the pool state without leaking the depositor's identity

Tech Stack

Layer Technology
Smart Contracts Sui Move
ZK Circuits Circom 2 + Groth16 (BN254)
On-chain Verifier Arkworks BN254 (Move)
Backend Node.js + Express + TypeScript
Frontend Next.js + TypeScript
Browser Proving snarkjs + circomlibjs

License

MIT

About

A privacy-preserving token transfer protocol on Sui, inspired by Tornado Cash and Mixoor. Users deposit SUI into a shared pool and withdraw to any address with zero on-chain link between sender and receiver.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages