Writz Protocol - Testnet Deployments
Network: Soroban Testnet (Test SDF Network ; September 2015)
Deployer: writz-deployer
Current deployment (2026-08-19)
Redeployed all four contracts together after the Elliot code-quality pass:is_borrow soundness fix + collateral/price range checks in the ZK circuits
(new verification keys, since the constraint system changed), the
Config.paused flag, the set_spv_contract/set_zk_verifier/set_oracle
setters, supply/withdraw events, and the oracle-stub dedup into
spv-types, per docs/architecture/contract-migration-runbook.md § Track 1
(planned migration, no funds at risk on the superseded testnet contracts -
straight redeploy rather than a wind-down). Built via stellar contract build (wasm-opt applied). Per the immutability ADR
(docs/architecture/adr/0001-contract-immutability.md) there is no in-place
upgrade path, so this is four fresh Contract IDs, not an upgrade of the ones
below.
bitcoin-spv
Checkpoint anchored to Bitcoin Signet tip at deploy time:
Verified:
get_checkpoint reads back exactly these values. ✓ The checkpoint must be refreshed periodically (recommended: weekly) - see docs/security/security-model.md for the full trust-model discussion. Before mainnet, convert the admin account to a multisig (2-of-3) rather than a single key.
zk-verifier
Verification keys set (regenerated from the range-checked circuits - IC lengths unchanged, but the key material itself differs from the superseded deployment since the constraint system changed):
Verified:
get_verification_key --circuit BorrowRepay reads back IC length 9 with all four curve-point fields populated. ✓
Note: These keys are from the development trusted setup (pot15). They are for testnet only and will be replaced by the multi-party ceremony keys before mainnet.
commitment-tree
Configuration
Verified:
get_merkle_root → "2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e", matching the Poseidon-2 empty Merkle tree root at depth 20. ✓
Pool seeded: 500 USDC supplied by writz-deployer - 5241a90817.... get_pool_state → total_supplied: 5,000,000,000 stroops (500 USDC), total_borrowed: 0. ✓
private-lend
Configuration
Verified:
get_protocol_state → total_supplied: 0, total_borrowed: 0, last_keeper_heartbeat set to init time. ✓
Pool seeded: 500 USDC supplied by writz-deployer - baf32ae36b.... get_protocol_state → total_supplied: 5,000,000,000 stroops (500 USDC). ✓
End-to-End ZK Flow Test ✓ (2026-08-19, against the current deployment)
Test instance:CCCT2CQUOMICS3IULYYQKCZ6PBUMUKE7EA77NQPDHHHVLUPLNOVQR2UU
Token: XLM native SAC (CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC)
Running scripts/deploy/e2e_zkflow.js against the new zk-verifier
(CBNZU23Q…) surfaced a real gap: the script’s synthetic single-tx-block
header had always used bits = 0/nonce = 0 (no actual mining), which the
current bitcoin-spv correctly rejects with InsufficientProofOfWork -
this only ever “worked” historically against a build that didn’t yet
enforce real proof-of-work. Rather than weaken the shared production
bitcoin-spv’s checkpoint (which is anchored to real Bitcoin Signet
difficulty specifically to reject easy fake chains), the script now deploys
its own throwaway bitcoin-spv per run with an easy checkpoint
(EASY_TEST_BITS = 0x207fffff, same constant the Rust test suite uses) and
mines its test header for real against it. Production bitcoin-spv’s
security is unaffected.
What this confirms: the freshly-generated
borrow_repay verification
key (with the is_borrow soundness fix + the new collateral/price range
checks, both from the Elliot pass) correctly verifies real Groth16 proofs
from the current circuits - deposit, borrow, and a full repay all passed
on-chain against zk-verifier CBNZU23Q…. ✓
Follow-ups this redeploy did not do
- Doc sweep: done - every non-historical reference to the superseded Contract IDs across
docs/,README.md,docs.json,mint.json,relayer/README.md, andpackages/commitment-tree/README.mdnow points at the current deployment;scripts/check-docs-sync.mjsreports zero drift. Test counts across all docs were re-synced fromdocs/_data/facts.json(191 contract tests + 60 bitcoin-script + 59 relayer + 29 circuits = 339 total) viascripts/update-test-counts.mjs. - Relayer live
.env: no relayer instance’s real.envexists in this checkout (only.env.example, which had no hardcoded contract IDs to begin with) - nothing to update. If a deployed relayer instance exists elsewhere, its env needs the newCOMMITMENT_TREE_ID/PRIVATE_LEND_IDby hand. docs/developers/contract-reference.mdand a couple of quick-start/milestone-plan snippets document function signatures and CLI calls (set_vkey,verify_groth16, commitment-tree/private-lendinitializeargument lists, abitcoin-spv::get_versioncall) that don’t match the actual current contract interfaces - this predates this redeploy and is a separate, larger doc-accuracy gap (API reference, not contract addresses). Contract IDs and test counts in those files are now correct; the signatures themselves were not rewritten here - flag if a full rewrite is wanted.
Superseded deployment (2026-06-22 / 2026-06-30)
Left running per the migration runbook’s default (no forced state transplant); kept here for history. No known funds are at risk on these - they predate real usage.bitcoin-spv
Verified call
txid and block_hash match SHA256d values exactly. ✓
[SUPERSEDED] This deployment predates the current contract build (pre-dates theis_borrow/range-check circuit fixes, thepausedflag, and theVerificationResult→SpvVerificationResultrename). Use the “Current deployment” section above instead.
zk-verifier (superseded)
Verification keys set
[SUPERSEDED] VKs here are from the pre-range-check circuits - proofs generated by the current circuits/ tree will NOT verify against this instance. Use the “Current deployment” section above.
commitment-tree (superseded)
Configuration
Verified call
"2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e"
Verified: matches the Poseidon-2 empty Merkle tree root at depth 20. ✓
[SUPERSEDED] Predatespaused/setters/supply/withdrawevents and the currentzk-verifier’s keys. Use the “Current deployment” section above.
private-lend (superseded)
[SUPERSEDED] Predatespaused/setters/supply/withdrawevents and the keeper stale-window liquidation fallback. Use the “Current deployment” section above.
P2WSH End-to-End Test (Bitcoin Signet)
Script:bitcoin-script/scripts/e2e_testnet.mjsRun:
node scripts/e2e_testnet.mjs --dry-run · node scripts/e2e_testnet.mjs (live broadcast)Date: 2026-06-23
Network: Bitcoin Signet (Blockstream Esplora)
Deposit address
Live broadcast results ✓
What was verified:
- P2WSH address derived correctly from (protocol_key, user_key, CLTV=700,000) ✓
- Both keys signed the PSBT independently (multi-party flow) ✓
finalizePathAassembled witness[user_sig, protocol_sig, 0x01, redeemScript]correctly ✓- Transaction broadcast and accepted by Bitcoin Signet mempool ✓
End-to-End ZK Flow Test ✓
Test instance:CDCH7C5TBJOZWIUKVSDGLWPTLTBIC55WH4447ZVIOA4NRDWKOXPVKOHBToken: XLM native SAC (
CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC)Date: 2026-06-22
What was verified on-chain:
- Groth16 deposit proof: BN254 pairing check via
bn254.pairing_checkhost function ✓ - Commitment inserted into Poseidon Merkle tree (root updated) ✓
- Groth16 borrow proof: collateral ratio enforced by ZK circuit (150% min) ✓
- USDC (XLM) transferred from pool to borrower ✓
- Groth16 repay proof: field-negation repay amount recovered correctly ✓
- Pool accounting updated after each operation ✓
- All proofs verified against
zk-verifiercontractCDV45GLXG4AOU6BDZSY5YHHVNGQIAYAPD3PUGXIIIYLIO6V2XGO6SMFV✓
Re-run after the enc_note interface change (#18)
The 2026-06-22 run above predates enc_note. e2e_zkflow.js was not updated
alongside the contract, so it failed at the deposit step until fixed (#15).
Re-verified against the current interface:
Test instance: CBM5OUBYBICB3QB4T5PAGYUWWLZOIVWQCUHKV3HCSNZGB72GYM5Q5ID4
Token: XLM native SAC (CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC)
Date: 2026-07-30 · whole run completes in about a minute
Reproduction steps, build prerequisites, and the trusted-setup caveat:
docs/developers/runbook.md.