ZyphBridge
Cross-chain transfers that preserve privacy.
Created on 4th December 2025
•
ZyphBridge
Cross-chain transfers that preserve privacy.
The problem ZyphBridge solves
Bridging Privacy-Preserving Blockchains
Zcash users have access to industry-leading privacy through shielded transactions, but their assets are siloed within the Zcash ecosystem. Meanwhile, Miden offers a new paradigm of private, scalable computation through zero-knowledge proofs. ZyphBridge solves the critical problem of moving value between these two privacy-focused chains.
Key Use Cases:
- Private DeFi Access: Zcash holders can now access Miden's private smart contract ecosystem without sacrificing privacy at the bridge layer
- Cross-Chain Privacy Preservation: Unlike most bridges that expose transaction details, ZyphBridge maintains privacy guarantees on both sides—shielded ZEC transactions on Zcash, private notes on Miden
- Unified Liquidity: Enables ZEC liquidity to flow into Miden dApps while maintaining the privacy properties users expect
How It Works:
- Deposit (ZEC → wZEC): Users send shielded ZEC to the bridge's Zcash address. Once confirmed, the bridge mints equivalent wZEC tokens on Miden as private notes
- Withdraw (wZEC → ZEC): Users burn wZEC on Miden, then claim their ZEC which is sent to their shielded Zcash address
The bridge operator monitors both chains, verifying deposits/burns and executing the corresponding mint/send operations—all while preserving the privacy guarantees of both networks.
Challenges I ran into
1. Miden Wallet Address Format Mismatch
The Bug: The bridge UI showed
0 wZEC
balance even after successfully minting tokens via the faucet.Root Cause: The Miden wallet adapter returns asset faucet IDs in bech32m format (e.g.,
mtst1azwuvc3clmzrqgz988cg09clssuq4jsq_qruqqypuyph
), but our code was comparing against hex format (0x9dc66238fec430204539f087971f84
).Solution: Implemented bech32m decoding in the frontend to convert wallet-returned addresses to hex for comparison:
const decoded = bech32m.decode(addressPart, 90); const bytes = bech32m.fromWords(decoded.words); const hex = '0x' + bytes.map(b => b.toString(16).padStart(2, '0')).join('');
2. Wallet Transaction API Confusion
The Bug: Burn transactions failed with
INVALID_PARAMS: Invalid CustomTransaction payload
.Root Cause: The Miden wallet adapter has multiple transaction methods. Using
requestTransaction()
with a genericTransaction
wrapper didn't work for send operations.Solution: Used
adapter.requestSend()
directly with a properly typedMidenSendTransaction
object, and discovered all address fields must be in bech32 format (not hex):const sendTransaction: MidenSendTransaction = { senderAddress: address, // bech32 recipientAddress: WZEC_FAUCET_ADDRESS, // bech32 faucetId: WZEC_FAUCET_ADDRESS, // bech32 noteType: 'private', amount: amountInSmallestUnits, }; await adapter.requestSend(sendTransaction);
3. Zcash Shielded Balance Timing
The Bug: After claiming a withdrawal,
z_getbalance
returned0
even though the transaction was sent.Root Cause: Zcash shielded (Sapling) transactions require block confirmations before the balance is spendable and visible.
Solution: Mine at least one block after the withdrawal transaction before checking balance. In production, the UI should poll or wait for confirmations.
Tracks Applied (1)
Cross-Chain Privacy Solutions
Miden
Cheer Project
Cheering for a project means supporting a project you like with as little as 0.0025 ETH. Right now, you can Cheer using ETH on Arbitrum, Optimism and Base.