Protocol docs
How ZkVeil works
A design specification for private wallet-to-wallet transfers routed through the Zcash shielded pool.
On this page
Overview
ZkVeil moves value from wallet 1 to wallet 2 without leaving a public link between them. Instead of a direct transfer, funds pass through a one-time shielded ZEC note in Zcash’s Orchard pool. Two atomic swaps carry them in and out, with no custodian, bridge or wrapped token.
The problem. On a transparent chain, sending from one wallet to another records the link forever. Anyone can follow it from your public wallet to your savings, your payroll or your trading account, and watch every balance and move after it.
What ZkVeil does instead. Leg 1 swaps the funds into ZEC held at a fresh shielded address that only your keys control. Inside the pool, amount and addresses are encrypted. After a random delay, leg 2 swaps the ZEC out and pays wallet 2. Observers see money leave wallet 1 and money arrive at wallet 2, with nothing connecting the two.
Design goals
- Unlinkable. Chain data alone should not connect wallet 1 to wallet 2.
- Trustless. No custodian, federation, multisig committee or oracle can take or freeze funds, at any point of the route.
- Atomic per leg. Each swap ends in exactly one of two states: both sides paid, or both sides refunded.
- Shielded in the middle. The ZEC hop never touches a transparent address.
- Non-custodial infrastructure. Relays and watchtowers pass messages and broadcast pre-signed transactions. They never hold keys that can move funds.
This page is a design specification. Parameters marked proposed may change before mainnet and after the security audit.
How a transfer works
A private transfer is two atomic swaps chained through a shielded hop. Only the two outer legs are visible on public chains.
- Leg 1: into the pool. Wallet 1 swaps its asset (for example 1 ETH) with maker A for ZEC. The ZEC lands at a one-time Orchard address generated by your app.
- The hop. The ZEC waits in the shielded pool for a random 0–20 minutes. To everyone else it is one of thousands of identical, encrypted Orchard notes.
- Leg 2: out of the pool. The hop note is swapped with maker B for the asset wallet 2 should receive. A relayer submits the final claim, so wallet 2 never needs gas or prior funding.
The app picks different makers for the two legs by default, so no single maker sees both wallets.
Core concepts
ZkVeil combines five building blocks. Together they let a transfer pass through shielded Zcash without trusting anyone along the way.
Orchard shielded pool. Zcash’s current shielded protocol. Notes (coin records) are encrypted on-chain, so amounts, senders and receivers are hidden. Shielded notes cannot carry scripts, so classic hashlock/timelock contracts are impossible inside the pool. ZkVeil works around this with the tools below.
Hop note. The ZEC between the two legs, held at a one-time diversified Orchard address. Your app derives its keys from a fresh session key and stores them encrypted on your device. You can export them at any time; nobody else can spend the note.
Shielded escrow note. A normal Orchard note whose spend-authorizing key is split between you and the maker using FROST, a threshold Schnorr signature scheme that supports Zcash’s RedPallas signatures. Each leg uses one. Neither party can spend it alone, and to everyone else it is indistinguishable from any other Orchard note.
Adaptor signature and secret binding. A partial signature becomes valid only when completed with a secret t, and publishing it lets the other party compute t. A Halo 2 zero-knowledge proof (no trusted setup) shows the same t sits behind the lock on the other chain, for example H = SHA-256(t) in VeilEscrow and T′ = t·G on Pallas. Each leg has its own secret, so the two legs share nothing on-chain.
Pre-signed refund. Before any funds move in a leg, both parties co-sign a refund for every escrow, each with a lock time. If the leg stalls, the refund becomes broadcastable after its timelock without any cooperation.
Glossary
| Term | Meaning |
|---|---|
| Wallet 1 | The wallet the transfer is sent from |
| Wallet 2 | The wallet that receives the transfer, ideally a fresh address |
| Hop note | The ZEC held between legs at a one-time shielded address under your keys |
| Leg 1 / Leg 2 | The atomic swap into the hop note, and the one out of it |
| Maker | A liquidity provider node that quotes prices and fills one leg |
| Initiator | The party that generates a leg’s secret t and locks funds first |
| T1 / T2 | Timelocks on the initiator's lock (longer) and the responder's lock (shorter) |
| Veil Relay | Message network carrying quotes and protocol messages between parties |
| Watchtower | Optional service that broadcasts pre-signed refunds if you go offline |
| VeilEscrow | The ZkVeil hash-time-lock contract deployed on EVM chains |
Architecture
ZkVeil has three off-chain components and one escrow type per chain. Only escrows and your hop note ever hold funds.
Your app and the maker nodes talk only through Veil Relay, then each settles directly on-chain. Watchtowers sit beside the chains and can only broadcast transactions both parties already signed.
- ZkVeil app (web, desktop, mobile): connects wallet 1, holds the hop keys locally, generates swap secrets and Halo 2 proofs, runs FROST signing, ranks quotes and drives both legs.
- Veil Relay: a set of independent relay nodes forming a store-and-forward message board for RFQs and end-to-end encrypted protocol messages. Anyone can run one. Relays see only ciphertext.
- Maker node: quotes prices, manages inventory on both chains and runs the same protocol state machine as the app. A maker fills one leg at a time.
- Chain adapters: one per chain, wrapping the escrow type in the diagram. New chains are added by writing an adapter, not by changing the core protocol.
Transfer lifecycle
The example below is Alice sending 1 ETH from wallet 1 to a fresh wallet 2. Maker A fills leg 1 and maker B fills leg 2. At every step, either Alice ends up with her funds at the next stage or gets them back.
- Estimate. The app prices the full route from live market rates and typical spreads, and shows a range for what wallet 2 will receive. Alice confirms the amount and wallet 2.
- Leg 1: quote and setup. The app broadcasts an anonymous request for quote (RFQ) over Veil Relay for ETH → ZEC. Makers reply with signed quotes valid for about 30 seconds and the app picks the best. Alice’s app is the initiator: it generates secret t1, runs FROST key generation for the escrow note with maker A, co-signs maker A’s refund and receives an adaptor pre-signature for her claim.
- Leg 1: lock and claim. Alice locks 1 ETH in VeilEscrow with hashlock H1. After enough confirmations, maker A funds the shielded escrow note. Alice’s app completes the adaptor signature with t1 and claims the ZEC into the hop note. Maker A extracts t1 and collects the ETH.
- Hop. The ZEC waits in the hop note for a random 0–20 minutes. Nothing about it is visible on-chain.
- Leg 2: quote and setup. The app requests fresh ZEC → ETH quotes, skipping maker A. Roles mirror leg 1: maker B is the initiator and generates t2. The app verifies maker B’s proof, runs FROST key generation, co-signs its own ZEC refund and gives maker B an adaptor pre-signature.
- Leg 2: lock and claim. Maker B locks ETH in VeilEscrow payable to wallet 2. After confirmations, the app funds the escrow note from the hop note. Maker B claims the ZEC, revealing t2, and a relayer claims the ETH for wallet 2 with t2, deducting the gas from the output. The transfer is complete.
Timelock rules
Each leg follows the same rule: the responder’s lock (T2) expires well before the initiator’s (T1).
| Leg | Initiator's lock (T1) | Responder's lock (T2) | Values proposed |
|---|---|---|---|
| Leg 1 (into ZEC) | Your ETH in VeilEscrow | Maker A's ZEC escrow note | T1 24 h, T2 12 h |
| Leg 2 (out of ZEC) | Maker B's ETH in VeilEscrow | Your ZEC escrow note | T1 24 h, T2 12 h |
The gap guarantees the responder always has at least 12 hours to claim after the initiator does. The initiator’s app refuses to claim once T2 is less than 1 hour away, which closes the last-second race. Bitcoin locks use 144 blocks for T1.
Privacy model
The hop hides the link between the wallets. The two outer legs stay public on their own chains, so ZkVeil also works to stop them being matched by amount or timing.
| Data | Public chain observer | Maker A (leg 1) | Maker B (leg 2) | Veil Relay operator |
|---|---|---|---|---|
| Wallet 1 and amount sent | Visible | Visible | Hidden | Hidden |
| Wallet 2 and amount received | Visible | Hidden | Visible | Hidden |
| Hop note amount and address | Hidden | Amount only | Amount only | Hidden |
| Link between wallet 1 and wallet 2 | Not derivable from chain data | Hidden | Hidden | Hidden |
| Your IP address | Not applicable | Hidden behind the mixnet | Hidden behind the mixnet | Hidden behind the mixnet |
What can still leak and how we reduce it
- Amount correlation. If 1 ETH leaves wallet 1 and 0.988 ETH reaches wallet 2, the two could be matched. Fixed amounts (0.1 / 1 / 10 ETH and equivalents) make your transfer look like many others, and receiving a different asset at wallet 2 breaks the match entirely.
- Timing correlation. A leg-1 lock followed closely by a leg-2 payout is a hint. The random 0–20 minute delay in the pool widens the window to every transfer in that period.
- Gas funding links. Funding wallet 2 with gas from wallet 1 would link them directly. Relayers submit the final claim and take the gas from the output, so wallet 2 never needs pre-funding.
- One maker seeing both sides. A maker filling both legs could match them. The app uses different makers for each leg by default.
- Network metadata. All RFQ and protocol messages go over Veil Relay through the Nym mixnet (Tor as fallback). Makers see a session key, not an IP address.
- Reusing wallet 2. A wallet 2 that has received funds from wallet 1 before is already linked. Use a fresh address; the app blocks sending to wallet 1 itself.
Out of scope: a compromised device, and on-chain activity before or after the transfer that you link yourself, such as sending funds from wallet 2 back to wallet 1.
Security and failure handling
The worst case for an honest user is a delay plus network fees, never lost funds. Every escrow has a pre-signed refund, and between the legs your ZEC sits in a note only your keys can spend.
Failure scenarios
| What goes wrong | Outcome | Who pays |
|---|---|---|
| Maker A never funds the ZEC escrow (leg 1) | Your ETH refunds to wallet 1 after T1 | You: gas; maker A's bond is slashed |
| You go offline during leg 1 | Your watchtower broadcasts your refund, or the app claims when you return before T2 | Nobody loses funds |
| No maker quotes leg 2 | ZEC stays in your hop note; the app retries, or you send it to any address you choose | Nothing extra |
| Maker B never locks (leg 2) | Your ZEC escrow refunds to the hop note after T2 | Zcash fees; maker B's bond is slashed |
| You close the app during the hop | Leg 2 starts when you reopen it; the ZEC waits in the hop note | Nothing extra |
| Relay goes down mid-transfer | Parties fall back to on-chain refunds | Fees only |
| Chain reorg on a first lock | Responders only lock after 12 ETH / 3 BTC confirmations proposed | Nobody |
Griefing and the free-option problem
In each leg, the initiator could lock and then wait, effectively holding a free option on price movement until T2. ZkVeil limits this in three ways:
- Short T2 windows (12 h) and quotes that expire in 30 seconds if not locked.
- An initiator who abandons a funded leg pays a small cancellation fee (0.1%, proposed) to the maker on refund.
- Makers post a bond. A maker who signs a quote and then fails to lock is slashed using the signed quote plus the initiator’s on-chain lock as evidence.
Price movement between legs
Leg 2 is quoted after the hop, so the amount reaching wallet 2 can move with the ZEC price during the delay. The app shows a range before you confirm. Turning the delay off shortens this window at some cost to privacy.
Watchtowers
Watchtowers only hold pre-signed refund and claim transactions for one leg. They cannot redirect funds, because every transaction they hold pays a fixed destination. Leg 2 always needs your app online to sign, so a watchtower never moves your hop note. Users can run their own, use the default public set, or turn watchtowers off.
Audits and verification
- The FROST/RedPallas adaptor-signature scheme and the cross-curve proofs get an independent cryptography review before mainnet.
- VeilEscrow contracts are immutable (no admin keys, no upgrade proxy) and audited by two independent firms.
- Bug bounty: up to $250,000 for critical issues proposed.
- App and maker code are open source and reproducibly built.
Liquidity, makers and fees
Liquidity comes from independent maker nodes competing on price through RFQ, not from a shared pool. A pool would need a custodian or a shielded smart contract, and Zcash doesn’t have one.
Becoming a maker. Anyone can run the open-source zkveil-maker node. It needs:
- Inventory on both sides (shielded ZEC plus the assets it quotes).
- A bond of at least 5,000 USDC-equivalent locked in the VeilBond contract proposed, scaled to the maximum quote size.
- A price feed and a pricing strategy. The reference node ships with a configurable spread over a median CEX price.
How quotes are chosen. For each leg the app gathers quotes for 2 seconds and ranks them by net output after fees. It skips makers with a recent slash, a fill rate under 95%, or that filled the other leg of the same transfer. Fill rate is tracked by session-unlinkable attestations, not by user identity.
Fees
A transfer pays each swap’s fees once per leg.
| Fee | Rate proposed | Paid to |
|---|---|---|
| Maker spread | Market-set, typically 0.2–0.6% per leg | Makers |
| Protocol fee | 0.15% of value per leg | ZkVeil treasury (audits, relays, bug bounty) |
| Zcash network fees | One shielded transaction fee per leg | Zcash miners |
| Lock gas | Normal gas for leg 1's lock, from wallet 1 | Validators |
| Gas relay (wallet 2 claim) | Actual gas + 10%, deducted from output | Relayer |
At typical spreads, a transfer costs about 1.1% of its value plus gas. The app shows the full breakdown and a range for the amount received before you confirm. Refunds carry no fees beyond network fees and, for abandoned legs, the cancellation fee described in Security.
Supported assets and roadmap
Every transfer hops through ZEC (Orchard). Wallet 1 and wallet 2 can use any supported chain, and they don’t have to be the same one.
| Chain | Assets | Lock type | Status |
|---|---|---|---|
| Ethereum | ETH, USDC, USDT | VeilEscrow hash-time-lock contract | Phase 1 |
| Bitcoin | BTC | Taproot adaptor signature + timelocked refund path | Phase 1 |
| Arbitrum, Base | ETH, USDC | VeilEscrow | Phase 2 |
| Solana | SOL, USDC | VeilEscrow (Anchor program) | Phase 3 |
| Monero | XMR | Adaptor signatures, both sides scriptless | Research |
Phases
- Phase 0: Testnet. Ethereum and Bitcoin transfers via Zcash testnet, public maker test program, cryptography review.
- Phase 1: Mainnet beta. Ethereum and Bitcoin transfers with per-transfer caps (proposed: 5 ETH / 0.25 BTC or equivalent), whitelisted makers, watchtower network live.
- Phase 2: Open makers and L2s. Permissionless maker bonding, caps lifted, Arbitrum and Base, mobile app.
- Phase 3: More chains. Solana, a public SDK for wallets to embed ZkVeil transfers, and research into Monero routes.
FAQ
Do I end up holding ZEC?
Only for the length of the hop. ZEC sits in a one-time shielded address controlled by your own keys, then leg 2 swaps it out to wallet 2. You never need to buy, manage or sell ZEC yourself.
Is ZkVeil a bridge?
No. Nothing is wrapped or minted. Wallet 1 sends native assets and wallet 2 receives native assets on their own chains.
Can ZkVeil freeze or take my funds?
No. There are no admin keys on any escrow. Every escrow pays out only to the two swap parties, via a joint signature, the secret, or a timelock refund. The hop note is spendable only with your keys.
Do I need KYC or an account?
No. You connect wallet 1, enter wallet 2 and confirm. Sessions use one-time keys.
How long does a transfer take?
Typically 30–90 minutes: two swaps of 15–45 minutes each, mostly waiting for confirmations, plus the optional random delay of up to 20 minutes.
What happens if I close the app mid-transfer?
During leg 1, your refund is pre-signed and a watchtower broadcasts it if needed. During the hop, your ZEC waits safely in the shielded note. Leg 2 starts when you reopen the app, because it needs your keys to sign.
Can wallet 2 receive a different asset?
Yes. Leg 2 can pay out any supported asset, so you can send ETH from wallet 1 and receive USDC at wallet 2.
Is my transfer fully anonymous?
Nothing on-chain links the two wallets, but matching amounts and timing can hint at a connection. Use fixed amounts, keep the random delay on and use a fresh wallet 2. See the Privacy model.
Ready to send privately?
Launch app