The Stellar Side
Four contracts, one system — how Writz lives on Soroban. The Stellar side of Writz consists of four Soroban smart contracts, a USDC liquidity pool, an interest rate model, and an oracle layer. This page walks through each component, how they interact, and the design decisions behind them.The Four Contracts
1. bitcoin-spv
Verifies Bitcoin transactions on Soroban using Simplified Payment Verification. Completely stateless — no Bitcoin headers are stored on-chain. Takes a proof bundle (headers + Merkle proof + raw transaction) and returns aVerificationResult containing the txid, block hash, and confirmed outputs.
This contract is called first in every deposit flow. Its output — specifically the txid — is passed to the commitment-tree contract and bound into the ZK proof, ensuring every position corresponds to a real Bitcoin transaction.
Deployed: CAE5L7BO2GNF7MIZWXB2BTUMLYNIMQZUSWN2BWLZQS7HRHLOUSL6VLWJ
2. zk-verifier
Verifies Groth16 BN254 proofs on Soroban using Protocol X-Ray’s BN254 host functions. Stores one verification key per circuit type (Deposit, BorrowRepay, Liquidation).
The core verification:
- Loads the verification key for the specified circuit
- Computes
vk_x = Σ (public_signals[i] × vk.ic[i])usingbn254_g1_msm - Runs a 4-pair
bn254_pairing_check:e(A,B) == e(alpha,beta) × e(vk_x,gamma) × e(C,delta)
true if the proof is valid, false otherwise. Malformed proofs (invalid curve points) cause the host to reject the transaction entirely — the correct security behavior.
Deployed: CDV45GLXG4AOU6BDZSY5YHHVNGQIAYAPD3PUGXIIIYLIO6V2XGO6SMFVAll three verification keys are set on testnet (Deposit IC=6, BorrowRepay IC=9, Liquidation IC=6).
3. commitment-tree
The core privacy and lending contract. Manages the Poseidon Merkle commitment tree and all ZK-gated lending operations. This is where positions are created, loans are issued, and repayments are recorded. Key functions:- The
borrowamount is extracted from the ZK proof’s public signal — the caller cannot supply an arbitrary amount - The
repayamount is recovered from field-negation inversion of the proof’s delta signal - The
liquidateusdc_debtis extracted from the proof — the liquidator cannot inflate the debt they claim - Nullifier freshness is checked before any state change — double-spending is impossible
- Merkle root must match the current on-chain root — stale proofs are rejected
CC2OZ3LG5U6RE3U7QC2R5QMID5GHQBE7QXTJQ4ZSTP5W73WDTKQPRW7E
4. private-lend
A non-ZK lending skeleton that provides the borrowing and repayment interface without the ZK layer. Used for:- Phase 1 testing (simpler than the full ZK flow)
- A reference implementation showing the core lending mechanics
- Future: may be used as a “fast lane” for users who opt out of ZK privacy
CCLH2GJYG3QSHZJI7V7VK3DNMNK3I3QJCECBSFGX3AC6CK4I7EF7ZJ2G
Interest Rate Model
Writz uses a kinked utilization curve — the same model pioneered by Aave and Compound, adapted for Writz’s parameters.
Rate examples:
Interest accrues continuously. Every position-touching call (borrow, repay, liquidate) applies accrued interest before processing the action.
Oracle Design
Writz uses a multi-oracle approach for BTC/USD price feeds, with a median aggregation strategy to resist price manipulation. Primary oracle: RedStone (push model, SEP-40 interface)Secondary oracle: Pyth Network (pull model, SEP-40 interface) Staleness check: Price data older than 90 seconds is rejected. If both oracles are stale, borrowing and liquidation are paused until fresh prices are available. Manipulation resistance:
- Median of two oracles: a single oracle manipulation requires moving the median
- Liquidation smoothing: large liquidations can be executed in tranches to prevent single-block oracle manipulation attacks
Storage and TTL Management
Soroban’s storage has a time-to-live (TTL) system. Every storage entry has an expiration point; entries that are not accessed eventually expire and are deleted. Writz manages TTL carefully to ensure user positions never expire unexpectedly:
Permissionless refresh: All critical entries have public
refresh_* functions that extend their TTL. Any keeper — including Writz’s own keeper, a third-party keeper, or even the user themselves — can call these functions to prevent expiry. No permission required.
Events
All contract state changes emit structured events using Soroban’s#[contractevent] annotation:
Events are the primary mechanism for the Writz backend to detect loan repayments and trigger the BTC co-signing release on the Bitcoin side.
Contract Interactions: Full Deposit Flow
Next: Developer Quick Start →