Zurf
Zcash development as accessible as Ethereum
The problem Zurf solves
The Zcash Developer Experience Gap
Zcash has a critical developer experience problem: setting up shielded transaction support is exponentially more difficult than it should be. While Ethereum developers enjoy mature toolkits like Hardhat, Foundry, and Remix that enable rapid development and debugging, Zcash developers face:
-
Complex Setup Barriers
- Manual node configuration requiring deep protocol knowledge
- Hours spent wrestling with zcashd/Zebra configuration files
- No standardized local development environment
- Fragmented tooling across multiple repositories
-
Opaque Debugging Experience
- Zero-knowledge proofs are black boxes - when transactions fail, developers get cryptic error messages with no visibility into why
- No visual tools to understand shielded transaction structure
- Difficult to validate if nullifiers, note commitments, or viewing keys are correctly generated
- Impossible to quickly inspect what's happening inside encrypted notes
-
Slow Iteration Cycles
- Testing requires full node synchronization
- No deterministic testing environment for edge cases
- Chain reorganizations and specific scenarios are hard to reproduce
- Each test cycle takes minutes to hours instead of seconds
-
Steep Learning Curve
- Developers must understand advanced cryptography (nullifiers, Merkle trees, commitment schemes) before building anything
- No interactive tools to learn by experimentation
- Documentation assumes expert-level knowledge
- Newcomers struggle to identify "what building blocks to grab to achieve their goals"
How Zev Toolkit Solves This
Zev Toolkit is a comprehensive, visual developer toolkit that makes Zcash development as accessible as Ethereum development by providing:
๐ One-Command Local Development Environment
zev init # Complete Zcash testnet running in <5 minutes
- Automated Docker orchestration of Zebra/zcashd + lightwalletd
- Pre-funded test accounts (transparent and shielded)
- Fast block generation for rapid testing
- Zero manual configuration required
๐ Visual Transaction Inspector (Web UI)
- Paste any transaction hex or hash โ See complete breakdown with human-readable labels
- Visual diagrams showing flow between transparent and shielded pools
- Expandable sections for:
- Transparent inputs/outputs with address decoding
- Sapling spends (nullifiers, anchors, proofs)
- Sapling outputs (note commitments, encrypted ciphertexts)
- Orchard actions (for v5 transactions)
- Zero-knowledge proof metadata
- Memo fields (automatically decoded)
- Viewing key tester - Test if a viewing key can decrypt transaction notes
- Transaction diff mode - Compare working vs. failing transactions side-by-side
๐ ๏ธ Interactive CLI Transaction Builder
zev tx create # Guided prompts for building shielded transactions zev tx simulate # Test without broadcasting zev tx decode <txid> # Inspect any transaction
- Step-by-step transaction construction with validation at each step
- Support for transparent โ shielded, shielded โ shielded, and shielded โ transparent flows
- Memo field helpers with encoding validation
- Fee estimation before broadcast
- Save/load transaction templates for common patterns
๐งช Deterministic Testing Harness
zev test scenario reorg # Trigger chain reorganization zev test mine 10 # Mine exactly 10 blocks zev test snapshot # Save blockchain state
- Integration with darksidewalletd for reproducible testing
- Pre-built test scenarios (reorgs, confirmations, edge cases)
- Snapshot and restore blockchain state
- Perfect for CI/CD integration
Challenges I ran into
Challenges I Ran Into
Building Zev Toolkit required solving several deep technical challenges in the Zcash ecosystem:
1. Navigating Zcash's Complex Cryptographic Library Ecosystem
The Problem:
The Zcash Rust ecosystem consists of multiple interconnected crates (
zcash_primitives
,zcash_client_backend
,orchard
,sapling-crypto
,zebra-chain
) with different API philosophies and version compatibility constraints. Key challenges included:- API Opacity:
zcash_primitives::transaction::Transaction
is an enum with private fields, making it difficult to extract transaction components programmatically - Module Path Confusion: Key types like
ExtendedSpendingKey
exist in multiple crates (zcash_primitives::zip32
, standalonezip32
crate,zcash_client_backend::keys::sapling
) with incompatible APIs - Subtle Cryptographic Types: The
subtle
crate'sCtOption<T>
type requires calling.into_option()
before standard RustOption
methods - this isn't documented in most examples - Version Mismatches:
zcash_primitives
0.19 has breaking changes from 0.13, and many online examples use outdated APIs
How I Solved It:
- Systematic API Exploration: Used
cargo doc
to generate local documentation for all Zcash crates and cross-referenced their type definitions - Hybrid Approach: Used
zebra-chain
for transaction deserialization (cleaner API) andzcash_client_backend
for key derivation (most complete implementation) - Proper Type Conversions: Implemented correct handling of
CtOption<T>
โOption<T>
conversions:let sk = OrchardSK::from_bytes(seed) .into_option() // Convert CtOption to Option .ok_or_else(|| Error::InvalidSeed)?;
- Version Pinning: Locked all Zcash dependencies to compatible versions in workspace
Cargo.toml
Key Learning:
The Zcash Rust ecosystem is powerful but fragmented. Success requires understanding which crate to use for which purpose:
zebra-chain
for parsing,zcash_client_backend
for wallet operations,orchard
/sapling-crypto
for low-level cryptography.2. Implementing ZIP-32 Hierarchical Deterministic Key Derivation
The Problem:
Generating cryptographically valid Zcash shielded addresses requires implementing ZIP-32 (the Zcash equivalent of BIP-32 for Bitcoin). This involves:
- Sapling: Deriving extended spending keys from a seed, converting to extended full viewing keys, then generating payment addresses with proper diversifier handling
- Orchard: Using a different key derivation scheme with spending authorities and full viewing keys
- Bech32 Encoding: Sapling uses Bech32 (with checksum variant), while Orchard uses Bech32m (modified variant) - using the wrong one produces invalid addresses
The official documentation assumes deep cryptographic knowledge and doesn't provide complete working examples.
How I Solved It:
Implemented proper key derivation for both pools:
Sapling (ZIP-32):
Used ExtendedSpendingKey to derive master key from seed, converted to ExtendedFullViewingKey, generated default payment address, and encoded with Bech32 (critical: Sapling uses original Bech32, not Bech32m).
Orchard:
Used SpendingKey from bytes with proper option handling, derived FullViewingKey, generated address at index 0 with External scope, and encoded with Bech32m (critical: Orchard uses Bech32m, not Bech32).
Validation:
- Generated addresses are 78 characters (Sapling) and 77 characters (Orchard)
- Addresses pass Bech32/Bech32m checksum validation
- Successfully imported into ZecWallet Lite for testing
Key Learning:
Cryptographic correctness requires attention to subtle details. Using
Bech32
instead ofBech32m
for Orchard produces addresses that look valid but fail checksum validation.3. Parsing Multi-Version Zcash Transaction Formats
The Problem:
Zcash has evolved through 5 transaction versions (v1-v5), each with different binary formats:
- v1-v2: Legacy transparent-only (pre-Sapling)
- v3: Overwinter upgrade (expiry height added)
- v4: Sapling shielded transactions (most common on mainnet)
- v5: NU5 upgrade with Orchard pool support
Each version has:
- Different field layouts
- Optional shielded bundles (Sapling, Orchard)
- Varying serialization formats for proofs and signatures
Parsing requires:
- Reading version bytes to determine format
- Deserializing based on version-specific schema
- Handling missing bundles gracefully (v4 has no Orchard, v3 has no Sapling)
- Extracting nested cryptographic components (nullifiers, note commitments, ephemeral keys)
How I Solved It:
Used zebra-chain's robust deserialization which handles version detection automatically. The transaction is deserialized from bytes with proper error handling, TXID is computed correctly using zebra-chain's hash method, and components are extracted based on transaction variant using pattern matching to safely handle
Tracks Applied (1)
Privacy Infrastructure & Developer Tools
Zcash Community Grants
Technologies used