Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Writz Protocol

Bitcoin was built to be yours. Your loans should be too.

CITestsNetworkLicense: Apache 2.0

Live App · Docs · Relayer API

Writz is the first trustless Bitcoin lending protocol on Stellar. Lock real BTC directly from your Bitcoin wallet, borrow USDC on Stellar, and keep every position private - always.

No bridge. No custodian. No wrapped tokens. No public balance sheet.


What Makes Writz Different

WritzEvery other lending protocol
CustodianBitcoin Script is the custodianA company holds your BTC
PrivacyPrivate by default · ZK proofsYour position is a public billboard
BTCNative on-chain BTCWrapped token (WBTC, tBTC…)
Emergency exitCLTV timelock → reclaim aloneDepends on protocol availability

This Is Not a Whitepaper

As of August 2026, four contracts are live on Soroban testnet, 339 tests pass, and real Bitcoin transactions have been verified on-chain.

WhatStatus
Bitcoin SPV verification on Soroban✓ Live on testnet
ZK-private positions (Groth16 BN254)✓ Verified on-chain
P2WSH locking + co-signed BTC release✓ Broadcast on Bitcoin Signet
Poseidon Merkle commitment tree✓ Root updated on-chain
Full deposit → borrow → repay ZK flow✓ 6 sequential testnet transactions
339 tests across all modules✓ All passing

Live Testnet Contracts

ContractAddressWASMTests
bitcoin-spvCB2BD6QCSZVNZN5NLI7C5NF356WXVJDSXT6LVAQFWHHS4SZ4NCKKNIVA12.0 KB49
zk-verifierCBNZU23QGCZATJB2QMNF2K6IST2SVP7FSGCKASQNBULTWDWGANDBYLFY14.5 KB25
commitment-treeCDQCTFO3FK3M47QS47O2A4WLNPSQAQBSXBFPJ6RZEHFO5D7RY34FSBBP31.7 KB32
private-lendCAAWVMDRUPEJNELSQ6RU2VMVX5EJLQ2E77T7IXDWGMW4DGSNAGECGSWR36.0 KB85

Full deployment log, init transactions, and verified calls: contracts/deployments/testnet.md


System Architecture

System Architecture

The protocol operates across two blockchains. Bitcoin is the custody layer - BTC never leaves the Bitcoin network. Stellar is the execution layer - loan logic, privacy, and USDC flows all run on Soroban.

Four layers, each with a clear boundary:

  1. Bitcoin Network - user's BTC wallet locks funds into a P2WSH script. The script enforces two spending conditions; no third party can move the funds.
  2. Backend Services - a stateless SPV Relayer watches Bitcoin blocks and assembles proof bundles for Soroban. A ZK Prover runs in the browser (no server-side proving).
  3. Soroban Contracts - four contracts verify Bitcoin transactions cryptographically, verify ZK proofs, manage the Poseidon Merkle tree, and issue/repay USDC loans.
  4. Browser - all secrets stay on the user's device. ZK proofs are generated locally. The Stellar wallet signs Soroban transactions.

How It Works

1 - Deposit & Borrow

Deposit Flow

  1. User connects their Bitcoin wallet (Xverse) and a Stellar wallet (Freighter) to the Writz UI.
  2. The frontend derives a unique P2WSH address for this deposit (user public key + protocol public key + timelock).
  3. User sends BTC to that address on Bitcoin. The script is now live on-chain.
  4. After 6 confirmations, the SPV Relayer assembles a proof bundle: raw transaction, block headers, and Merkle inclusion path.
  5. The bitcoin-spv Soroban contract verifies the bundle cryptographically - no oracle, no trust.
  6. The browser generates a Groth16 ZK proof (deposit circuit): proves BTC was locked and a valid commitment exists, without revealing the amount.
  7. The commitment-tree contract verifies the ZK proof on-chain and inserts the commitment into the Poseidon Merkle tree.
  8. The user can now borrow up to 66% of BTC value in USDC from the private-lend pool.

2 - Borrow & Repay

Borrow / Repay Flow

Borrowing requires a ZK proof that the position's collateral ratio is above the minimum threshold. The proof reveals nothing about the actual amounts - only that the invariant holds. Repayment rotates the nullifier so the position cannot be double-spent.

3 - BTC Release

BTC Release

When the loan is fully repaid, the protocol co-signs a PSBT (Partially Signed Bitcoin Transaction) using its signing key. The user countersigns with their Bitcoin wallet and broadcasts. BTC arrives back in their wallet. The protocol never held custody at any point.


ZK Privacy Layer

Every position is private from the moment of deposit. The Soroban contracts verify loan validity without ever learning the amounts involved.

What Is Hidden

HiddenVisible
Collateral amount (BTC)Total protocol TVL (aggregate)
Loan amount (USDC)Total USDC outstanding (aggregate)
Health ratioThat a liquidation occurred (not who/how much)
User identityMerkle tree root

Three Groth16 Circuits

ZK Circuits

  • deposit.circom - Proves BTC was locked and a valid commitment exists. Public output: the commitment hash and the SPV verification result.
  • borrow_repay.circom - Proves the position is sufficiently collateralized for the requested borrow amount, and correctly computes repayment with interest.
  • liquidation.circom - Proves a position's health ratio fell below the liquidation threshold (120%). Anyone can trigger liquidation by providing this proof.

Circuits use Groth16 over BN254. Verification runs on Soroban via Protocol 26 host functions (bn254.g1_msm, bn254.pairing_check).

Position Lifecycle

Commitment State Machine


Smart Contract Architecture

Contract Interactions

ContractPurposeDepends on
bitcoin-spvSHA256d, PoW validation, checkpoint-anchored difficulty check, Merkle inclusion, block header chain-
zk-verifierStores Groth16 verification keys; verifies deposit/borrow_repay/liquidation proofs-
commitment-treePoseidon Merkle tree; core deposit/borrow/repay/liquidate logic, ZK-privatebitcoin-spv + zk-verifier
private-lendUSDC lending pool; supply, withdraw, interest rate model, plaintext (non-ZK) positionsbitcoin-spv

Interest rate model - kinked curve: base rate + linear slope up to 75% utilization, then a steep slope to discourage over-borrowing. Protocol captures the spread between borrow and supply rates.

Note on private-lend vs. commitment-tree: these are independent, parallel lending implementations against the shared bitcoin-spv/zk-verifier primitives, not a layered dependency - private-lend is the Phase 1 non-private MVP; commitment-tree is the ZK-private product described above, built as a standalone contract for cleaner separation of concerns rather than embedding ZK logic into private-lend. Each independently rejects a reused Bitcoin txid before creating a position/commitment - see docs/security/security-model.md for how this closes the Merkle duplicate-leaf ambiguity (CVE-2012-2459-class) at the deposit layer.


Bitcoin Script Design

Bitcoin P2WSH Script

The BTC locking mechanism lives entirely on Bitcoin. The P2WSH redeem script encodes two spending paths:

OP_IF
<protocol_pubkey> OP_CHECKSIGVERIFY
<user_pubkey> OP_CHECKSIG
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<user_pubkey> OP_CHECKSIG
OP_ENDIF

Path A - Cooperative release (normal case): Both the protocol and user sign. Triggered when the loan is repaid. The Soroban contract issues the co-signature only after verifying repayment on-chain.

Path B - Emergency recovery (timelock): After a predefined locktime (loan maturity + 30 days), the user can spend without any protocol involvement. If Writz disappears, the user's funds are never locked forever.

See bitcoin-script/ for the full P2WSH builder, address derivation, and PSBT signing toolkit.


Products

ProductDescriptionStatus
PrivateLendDeposit BTC as collateral → borrow USDC privatelyPhase 1 - testnet ✓
Dark SwapConvert BTC to USDC directly · no exchange · no visible orderPhase 3 - planned
BTC SavingsBTC collateral + USDC auto-routed to highest-yield Stellar poolsPhase 3 - planned
ZK Proof of ReserveProve BTC holdings without revealing wallets or amounts · B2B SaaSPhase 3 - planned

The Bitcoin SPV SDK is also open infrastructure. Any Stellar protocol that needs to verify a Bitcoin transaction on-chain can use bitcoin-spv with one call. Writz charges a per-verification fee.


Repository Structure

writz/
├── contracts/ # Soroban smart contracts (Rust)
│ ├── contracts/
│ │ ├── bitcoin-spv/ # SHA256d · PoW · Merkle inclusion · block headers
│ │ ├── zk-verifier/ # Groth16 BN254 · verification key store
│ │ ├── commitment-tree/ # Poseidon Merkle tree · ZK lending logic
│ │ └── private-lend/ # USDC pool · interest model · orchestration
│ └── deployments/
│ └── testnet.md # Live addresses · tx hashes · verified calls
│
├── circuits/ # ZK circuits (Circom 2.2.3 + snarkjs)
│ ├── src/
│ │ ├── deposit.circom
│ │ ├── borrow_repay.circom
│ │ ├── liquidation.circom
│ │ └── merkle.circom
│ └── keys/ # Verification keys (committed to repo)
│
├── relayer/ # SPV Relayer service (TypeScript · Express · Bun)
│ └── src/
│ ├── routes/proof.ts # GET /spv-proof/:txid
│ └── bitcoin/ # Esplora client · header fetching · proof assembly
│
├── bitcoin-script/ # Bitcoin locking script toolkit (TypeScript)
│ └── src/
│ ├── script.ts # P2WSH builder
│ ├── address.ts # Address derivation (testnet / mainnet)
│ ├── spend.ts # Path A/B PSBT signing
│ └── keys.ts # Key management
│
├── frontend/ # Next.js web app (React 19 · TypeScript · Tailwind)
│ └── src/
│ ├── components/ # DepositFlow · PositionDashboard · LenderPanel
│ ├── lib/flows/ # deposit · borrow · repay · recover · lend
│ ├── lib/position/ # Commitment derivation · note encryption
│ └── lib/prover/ # In-browser ZK proof generation (snarkjs)
│
├── packages/
│ └── commitment-tree/ # Generated TypeScript bindings for commitment-tree
│
├── scripts/
│ ├── deploy/ # Deployment scripts · e2e_zkflow.js · set_vkeys.js
│ └── diagrams/ # Graphviz architecture diagrams (Python)
│
└── docs/ # Full documentation (Mintlify)

Tech Stack

LayerTechnologyNotes
Smart contractsSoroban · RustProtocol 26 · soroban-sdk = "26"
ZK proofsCircom 2.2.3 · snarkjs · Groth16BN254 curve · in-browser proving
ZK on-chainProtocol 26 host functionsbn254.g1_msm · bn254.pairing_check
Bitcoin scriptingP2WSH (Phase 1) → Taproot (Phase 3)bitcoinjs-lib · ecpair
Bitcoin walletsXverse · sats-connectPSBT standard
FrontendNext.js 16 · React 19 · TypeScriptApp Router · Tailwind CSS 4
Stellar walletsStellar Wallets Kit · PrivyFreighter · Lobstr · email login
Relayer runtimeBun · Express.jsAlpine Docker · Esplora-backed
Merkle hashingPoseidon (poseidon-lite)Same in circuits + contracts + JS
CIGitHub Actions4 parallel jobs · all tests must pass

Quick Start

Prerequisites

# Rust with Soroban WASM target
rustup target add wasm32v1-none
cargo install stellar-cli --locked --version 27 # or later# Node.js / Bun (for relayer, bitcoin-script, circuits, frontend)
node --version # >= 20
bun --version # >= 1.1# For ZK circuit compilation only.# circom 2.x is a Rust binary - do NOT `npm install -g circom`, which installs# the legacy 1.x package and cannot compile `pragma circom 2.0.0`. Grab the# release binary (CI pins v2.2.3) or build it with cargo:
curl -fL -o ~/.local/bin/circom \
https://github.com/iden3/circom/releases/download/v2.2.3/circom-linux-amd64
chmod +x ~/.local/bin/circom # macOS: use circom-macos-amd64
circom --version # expect: circom compiler 2.2.3# snarkjs needs no global install - it is already a dependency of circuits/

Run All Tests

Each module has its own toolchain - there is no unifying root build, and the package manager is not the same everywhere. Run them from the repo root:

# 1. Soroban contracts - 191 testscd contracts && cargo test# 2. Bitcoin script toolkit - 60 tests (Bun's own test runner)cd ../bitcoin-script && bun install && bun test# 3. Relayer service - 59 tests# Deps install with Bun, but the suite itself is Jest (ts-jest), so it must# be run through the package script - plain `bun test` picks Bun's runner# instead and fails. The relayer also imports the local @writz/* packages# via their built dist/ output, so build those first.cd ../packages/commitment-tree && bun install
cd ../../bitcoin-script && bun run build
cd ../relayer && bun install && bun run test# 4. ZK circuits - 29 tests (npm + Jest; needs circom on PATH)cd ../circuits && npm install && npm test

All 339 tests pass. If anything fails, open an issue.

Full ZK End-to-End on Soroban Testnet

Deploys a fresh commitment-tree and runs the complete deposit → borrow → repay cycle with real Groth16 proofs:

WRITZ_DEV_SECRET=<your-testnet-key> node scripts/deploy/e2e_zkflow.js

Get a free testnet key and fund it with Stellar Friendbot.

This needs compiled circuit artifacts and a built contract wasm, neither of which is in git. Read the Testnet Runbook first - it covers the build chain, the trusted-setup caveat that otherwise makes proofs fail on-chain, the testnet assumptions (XLM stands in for USDC, the Bitcoin transaction is fabricated), and the manual Signet walkthrough for the Bitcoin half.

Frontend Dev Server

cd frontend
cp .env.example .env.local
# Fill in NEXT_PUBLIC_* contract addresses from contracts/deployments/testnet.md# Set NEXT_PUBLIC_RELAYER_URL=https://writz-relayer-production.up.railway.app
bun install && bun dev
# → http://localhost:3000

The testnet app is also live at writz.xyz.

Generate Architecture Diagrams

pip install graphviz
python3 scripts/diagrams/render-all.py
# → docs/diagrams/output/*.png + *.svg

Test Coverage

ModuleLanguageTestsHow to run
bitcoin-spv contractRust47cd contracts && cargo test -p bitcoin-spv
zk-verifier contractRust18cd contracts && cargo test -p zk-verifier
commitment-tree contractRust18cd contracts && cargo test -p commitment-tree
private-lend contractRust63cd contracts && cargo test -p private-lend
Relayer serviceTypeScript48cd relayer && bun run test
Bitcoin script toolkitTypeScript60cd bitcoin-script && bun test
ZK circuitsCircom / JS20cd circuits && npm test
Total274

Roadmap

Roadmap

Phase 1 - Foundation(current, Jul–Sep 2026)

  • 4 contracts live on Soroban testnet
  • Full ZK E2E cycle verified on-chain
  • P2WSH locking and release tested on Bitcoin Signet
  • SCF Build Award submitted (Open Track)
  • Trusted setup ceremony planned (5+ independent participants)
  • Docs live at docs.writz.xyz

Phase 2 - Launch(Q4 2026)

  • Audit Bank: Veridise (ZK circuits) + OtterSec (Soroban contracts)
  • Mainnet launch gated: $50K TVL cap, whitelist-only first 30 days
  • Frontend: full deposit / borrow / repay / repay UI with in-browser ZK proving
  • DeFiLlama listing on day 1

Phase 3 - Scale(2027)

  • Dark Swap: private BTC → USDC conversions
  • BTC Savings: auto-routed USDC yield (Blend, Phoenix DEX)
  • ZK Proof of Reserve: enterprise B2B attestation product
  • WRTZ governance token: fair IDO at $5M TVL, real-yield buyback mechanics
  • SPV SDK published as open Stellar ecosystem infrastructure

Business Model

Revenue StreamMechanism
Lending spreadBorrow rate minus supply rate on PrivateLend
Swap feesBasis points on each Dark Swap conversion
SPV API feesPer-verification or subscription for third-party Stellar protocol integrations
Proof of Reserve SaaSMonthly subscription for enterprise B2B customers
Insurance fund% of all protocol fees auto-routed to an on-chain reserve

Security

Risk Model

RiskMitigation
Bitcoin reorgRequire 6 confirmations before deposit is recognized
P2WSH script bugFormal review; emergency timelock protects users regardless
Protocol key compromiseMPC / HSM co-signing key; key rotation roadmap
SPV contract exploitExternal audit; stateless approach minimizes attack surface
Oracle manipulationMedian of multiple price feeds (Pyth + DIA)
ZK proof soundnessBattle-tested Groth16; production ceremony required before mainnet
Mass liquidation (BTC crash)Conservative 150% collateral ratio; open liquidation keeps keepers competitive

Audit Roadmap

AuditorScopeTiming
VeridiseZK circuits (Circom + proving keys)After SCF Tranche #1
OtterSec / ZellicSoroban contractsAfter SCF Tranche #2
InternalBitcoin P2WSH scriptingOngoing

Mainnet launch is gated on zero critical/high findings from both audits.

To report a security issue: open a private GitHub Security Advisory. Full policy, response targets, and safe harbour in SECURITY.md; rewards and severity bands in docs/security/bug-bounty.md.


Documentation

Full documentation lives in docs/ and is published at docs.writz.xyz:

Start here:

Products:

How it works (technical):

Developers:

Security:

Roadmap:

  • Vision - Where Writz is going by 2028.
  • Phases - Phase-by-phase execution plan.

Contributing

  1. Fork the repo and create a branch from main.
  2. Run the full test suite before opening a PR - all 339 tests must pass.
  3. For new features, add tests. For bug fixes, add a regression test.
  4. Open a PR with a clear description of what changed and why.

See docs/developers/contribution-guide.md for detailed guidelines.


Get Involved

You areStart here
BTC holder who wants to borrow USDC privatelyPrivateLend →
Developer who wants to build on the protocolQuick Start →
Stellar protocol that needs Bitcoin verificationSPV SDK →
Institution exploring ZK Proof of ReserveZK PoR →

License

Apache License 2.0 - see LICENSE.

About

Trustless Bitcoin DeFi on Stellar - zk private BTC collateral lending. Native BTC via Bitcoin Script + SPV on Soroban, positions hidden behind Groth16 proofs. No bridge, no custodian, no wrapped tokens.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages