Skip to content
home/overview/05 · disclosure

Prove one fact. Reveal nothing else.

The reason institutions avoid shielded pools is not that they dislike privacy — it is that they cannot answer an auditor from inside one. Viewing keys fix that, and Z-Relay turns them into something a smart contract can check.

The compliance deadlock

A desk holding a large shielded position faces two bad options. Move to a transparent chain and publish its book to every competitor with a block explorer. Or stay private and be unable to demonstrate solvency to a counterparty, an auditor or a lending protocol.

Both options assume disclosure is all-or-nothing. It is not. Zcash was designed from the start with a key hierarchy that makes partial, recipient-scoped disclosure a first-class operation.

The key hierarchy

zcash-key-hierarchy
Spending key (sk)          — spend funds.            Never shared. Ever.
 └─ Full viewing key (fvk) — see incoming + outgoing.  Shared with an auditor.
     ├─ Incoming (ivk)     — see incoming only.        Shared with a bookkeeper.
     └─ Outgoing (ovk)     — see what you sent.        Shared with a regulator.

Z-Relay never transmits any of these. The proof is built locally from the key
and only the proof leaves the browser.

A full viewing key lets its holder decrypt a wallet's notes and read their history. That is already better than publishing an address — it is disclosure to one party rather than to the world. But it is still coarse: an auditor who needs to confirm a single balance receives the entire transaction history along with it.

From viewing key to zero-knowledge claim

Z-Relay narrows the disclosure by one more step. Instead of handing over the key, the holder uses it locally to build a proof of a specific predicate:

  • Balance thresholds. “This wallet controls at least 100 ZEC as of block 2,654,900” — without revealing the actual balance.
  • Payment receipt. “A payment matching this invoice hash settled to me” — without revealing who else has paid me.
  • Settlement finality. “The trade referenced by this memo completed” — without exposing the counterparty.
  • Non-membership. “None of my notes originate from this sanctioned set” — without enumerating my notes.
The key never moves
The attestation circuit is compiled to WASM and runs in the user's browser. The viewing key is read into local memory, the witness is built there, and only the resulting Groth16 proof is transmitted. Z-Relay operates no prover fleet — there is no server to subpoena, breach or misconfigure.

What the contract sees

On the EVM side, an integrating protocol receives four values: a claim hash, the asserted threshold, the proof, and the Zcash anchor it was built against. Verification is a single view call.

PrivateCreditVault.sol
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.24;3 4interface IZRelayVerifier {5    function verifyViewingKeyAttestation(6        bytes32 claimHash,7        uint256 minBalance,8        bytes calldata zkProof9    ) external view returns (bool);10}11 12contract PrivateCreditVault {13    IZRelayVerifier public immutable verifier;14 15    event CollateralVerified(address indexed borrower, uint256 verifiedBalance);16 17    constructor(address _verifier) {18        verifier = IZRelayVerifier(_verifier);19    }20 21    /// @notice Unlock an undercollateralized USDG loan by proving a shielded22    ///         ZEC balance — without revealing the address or its history.23    function verifySolvencyAndBorrow(24        uint256 requestedLoan,25        bytes32 claimHash,26        uint256 minBalanceProof,27        bytes calldata proof28    ) external {29        bool ok = verifier.verifyViewingKeyAttestation(30            claimHash,31            minBalanceProof,32            proof33        );34        require(ok, "Z-Relay: invalid shielded balance proof");35 36        emit CollateralVerified(msg.sender, minBalanceProof);37        // ...continue with loan issuance on Robinhood Chain38    }39}
Verification cost
~180,000 gas
Proof payload
~192 bytes (3 BN254 group elements)
Freshness
Bound to a Zcash block anchor
Replay protection
claimHash includes the destination contract
Revocation
Proofs expire with their anchor window
Disclosure scope
One predicate, one verifier, one anchor

Binding a proof to its audience

A proof of solvency that any contract can consume is a proof that leaks. The claim hash therefore commits to the destination contract address, so an attestation generated for a lending vault cannot be replayed against a different protocol to unlock a second loan against the same collateral. Anchoring to a block height bounds it in time as well: an attestation is a statement about a moment, and it expires.

What this does not do

Selective disclosure is a tool for proving things to parties you have chosen. It does not make a wallet compliant, it does not screen counterparties, and it cannot un-share a key that has already been shared. It changes the unit of disclosure from “your entire financial history” to “the single fact this counterparty needs” — which is a large improvement and not a legal opinion.

Build it
The viewing-keys guide walks through generating an attestation in the browser and submitting it from the user's own wallet.