Zero-knowledge privacy layer for hospitality escrows on Stellar.
Booking amounts, wallet balances, and milestone releases are hidden inside Noir circuits. Only Pedersen commitments and proof hashes go on-chain β never raw numbers.
Stellar Hacks ZK Hackathon Β· Built on TrustlessWork Β· Powered by Noir / UltraHonk
| Layer | Status | Notes |
|---|---|---|
circuits/proof_of_funds |
β Compiles + proves | make prove-proof-of-funds |
circuits/private_escrow |
β Compiles + proves | make prove-private-escrow |
circuits/milestone_release |
β Compiles + proves | make prove-milestone-release |
sdk/ |
β Built + tested | @safetrust/zk-sdk β all 3 provers wired |
contracts/escrow_verifier |
β Soroban UltraHonk verifier | Rust, testnet-ready |
demo/ |
β Running | Next.js 14 Β· apartment booking flow Β· Freighter wallet |
docker-compose.yml |
β Monolithic | Postgres + demo in one command |
db/init.sql |
β Minimal schema | 3 tables only: escrows, milestones, zk_proof_log |
ZK_ENABLED hook |
β Implemented | Writes proof hashes to DB after each booking step |
git clone https://github.com/safetrustcr/safetrust-ZK.git
cd safetrust-ZK
docker compose up --build- Demo β http://localhost:3000
- PostgreSQL β
localhost:5433Β· db:safetrust_zkΒ· user:postgresΒ· pass:zkdemo
No backend-SafeTrust clone. No Hasura. No Firebase. The demo writes ZK proof hashes directly to the 3 tables in db/init.sql.
Every SafeTrust escrow today is fully transparent on Stellar:
- Competitors can see hotel pricing on-chain
- Guests expose travel spending patterns publicly
- Milestone amounts (70% check-in, 30% checkout) are visible to anyone
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π Casa Stellar, San JosΓ© CR Β· 150 USDC/night β
β β
β β Prove Funds β β
proof hash: 0xab12β¦ β
β balance β₯ 450 USDC β balance never revealed β
β β‘ Private Escrow β β
commitment: 0xfe34β¦ β
β amount = Pedersen(450 USDC) β amount hidden β
β β’ Check-in (70%) β β
release proof: 0xcd56β¦ β
β β£ Checkout (30%) β β
release proof: 0xef78β¦ β
β β
β π Receipt: Amount [HIDDEN π] Β· Raw DB: β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Only what the ZK pipeline actually reads and writes β defined in db/init.sql:
trustless_work_escrows β ZK writes proof_hash + commitment to escrow_metadata JSONB
escrow_milestones β ZK writes release_proof to metadata JSONB
zk_proof_log β append-only audit trail of every proof event
No users, no apartments, no bids, no roles. Seed data in demo/lib/seeds.ts replaces all of that.
Verify proofs landed after booking:
SELECT contract_id,
escrow_metadata->>'zk_proof_hash' AS proof_hash,
escrow_metadata->>'zk_amount_commitment' AS commitment
FROM public.trustless_work_escrows
WHERE escrow_metadata ? 'zk_proof_hash';
-- Full audit trail
SELECT circuit, proof_hash, commitment, created_at
FROM public.zk_proof_log
ORDER BY created_at DESC;| Circuit | Private inputs | Public inputs | Proves |
|---|---|---|---|
proof_of_funds.nr |
balance, randomness |
commitment, threshold |
balance β₯ threshold |
private_escrow.nr |
amount, view_key, addresses |
commitment, encrypted_amount |
amount committed + encrypted |
milestone_release.nr |
total_amount, randomness |
commitment, release_commitment, pct |
pct β {70, 30} is correct |
| Stored | ZK treatment | |
|---|---|---|
| Wallet address, contract ID, escrow status | β plaintext | Public by design |
| Proof hash, Pedersen commitment | β plaintext | Hash is safe; source stays private |
| Encrypted amount (ChaCha20) | β JSONB | Ciphertext only β view key required |
| Booking amount (raw USDC) | β never | Noir circuit private input only |
| Guest wallet balance | β never | Range proof only |
| ECDH view key | β never | On-device β guest + host + auditor |
| IP address | β not collected | ZK privacy principle |
Node.js β₯ 18 pnpm β₯ 9
Nargo β₯ 0.30 curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash && noirup
bb (matches) curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/next/barretenberg/bbup/install | bash && bbup
Freighter ext freighter.app (browser extension β for demo wallet)
Docker v2 full-stack only
pnpm install
# 1. Build the SDK first (required by demo API routes)
pnpm --filter @safetrust/zk-sdk build
# 2. Configure and start demo
cp demo/.env.example demo/.env.local
# Delete demo/.env if it exists β it overrides .env.local
pnpm --filter safetrust-zk-demo dev # β localhost:3000Important: Delete demo/.env if it exists. Next.js reads both files and .env takes priority, which can activate Pollar mode unintentionally.
Requires nargo and bb installed (see Prerequisites above).
make compile-all # compile all three circuits
make prove-proof-of-funds # compile β execute β prove β verify (Circuit 1)
make prove-private-escrow # compile β execute β prove β verify (Circuit 2)
make prove-milestone-release # compile β execute β prove β verify (Circuit 3)
make test-circuits # run nargo test suites
make test-all # circuits + SDK + Soroban contract testsEdit circuits/<name>/Prover.toml to test different amounts:
# circuits/proof_of_funds/Prover.toml
balance = "20000000000" # 2000 USDC β guest balance (private)
threshold = "4500000000" # 450 USDC β booking amount (public threshold)
randomness = "12345" # blinding factor (private)cd sdk
pnpm install
pnpm build # required before running demo
pnpm testimport { SafeTrustZK } from '@safetrust/zk-sdk';
const zk = new SafeTrustZK();
// 1. Prove solvency β balance never leaves the circuit
const { commitment } = await zk.proveOfFunds({
balance: 20_000_000_000n, // stroops (private)
threshold: 4_500_000_000n, // stroops (public threshold)
});
// 2. Commit booking amount privately
const { commitment: escrowCommitment } = await zk.commitEscrowAmount({
amount: 4_500_000_000n,
guestAddress: 'G...',
hostAddress: 'G...',
});
// 3. Prove milestone release (70% check-in)
await zk.proveMilestoneRelease({
amountCommitment: escrowCommitment,
totalAmount: 4_500_000_000n,
milestonePct: 70,
});// Before β amount public on-chain
await initializeEscrow(escrowParams);
// After β only Pedersen commitment on-chain
const { proof, commitment } = await zk.proveAndCommitEscrow({
balance, amount, guestAddress, hostAddress,
});
if (proof.valid) {
await initializeEscrow({ ...escrowParams, amountCommitment: commitment });
}| Layer | Technology |
|---|---|
| ZK circuits | Noir / Barretenberg UltraHonk |
| On-chain verifier | Soroban (rs-soroban-ultrahonk) |
| SDK | TypeScript β @noir-lang/noir_js, @aztec/bb.js |
| Demo | Next.js 14 App Router Β· Freighter wallet |
| Database | PostgreSQL 15 β 3 tables only (db/init.sql) |
| Blockchain | Stellar Testnet Β· TrustlessWork API |
| Wallet | Freighter β @stellar/freighter-api |
Not a mixer. The view key pattern means the guest, host, and any designated auditor can always reconstruct full transaction details. Everything else is confidential β not opaque.
See CONTRIBUTING.md and Git Guidelines. Issues tagged circuit Β· sdk Β· demo Β· contracts.
MIT Β© SafeTrust