Skip to content
PCZTKit

PCZTKit

One Toolkit. Infinite Private Flows.

Created on 1st December 2025

PCZTKit

PCZTKit

One Toolkit. Infinite Private Flows.

The problem PCZTKit solves

Most exchanges, custodial wallets, and simple services that support Zcash today only know how to build Bitcoin-style transparent transactions. They can’t easily send to shielded (Orchard) addresses without:

  • Re‑implementing complex Zcash cryptography.
  • Tracking consensus details (ZIP‑244, ZIP‑317, ZIP‑321, Orchard circuits).
  • Maintaining their own proving infrastructure.

As a result, transparent‑only users can’t easily pay into shielded, even if they want the privacy benefits.

PCZTKit solves this by:

  • Providing a Rust core around the official

    pczt

    crate, plus a TypeScript library and a JSON CLI.
  • Letting a transparent‑only system feed in:
    • Transparent UTXOs (

      txid

      ,

      vout

      ,

      amountZats

      ,

      scriptPubKey

      ,

      pubkey

      ).
    • A transaction request (recipients, amounts, optional memos / ZIP‑321 URIs).
  • Returning PCZT bytes that:
    • Include real transparent spends.
    • Can contain real Orchard outputs for shielded recipients.
    • Are compatible with the upstream

      pczt

      Prover and Extractor roles.

People can use PCZTKit to:

  • Add “send to shielded” to an existing transparent‑only wallet or exchange API, without touching Zcash circuits directly.
  • Keep their existing signing infrastructure:
    • We expose a ZIP‑244‑compatible

      getSighash

      helper.
    • Transparent signatures and broadcast remain the host’s responsibility.
  • Experiment and prototype with PCZT flows from multiple languages (Node.js, Go, later JVM or others) via the same JSON CLI.

This makes sending to shielded easier and safer for existing systems: they delegate the tricky PCZT/Orchard logic to a small, audited core built on top of ECC’s own libraries, instead of re‑inventing it.

Challenges I ran into

1. Signer role and

append_signature

limitations

One big challenge was doing signer‑related work in a way that respects the current upstream PCZT model:

  • Transparent signatures are stored in a private

    partial_signatures: BTreeMap<[u8; 33], Vec<u8>>

    inside

    zcash_transparent::pczt::Input

    .
  • That map is only written by

    Input::sign

    , which holds the secret key and computes the ZIP‑244 sighash internally.
  • There is no public API today to attach raw signature bytes to a particular transparent input after you’ve computed a sighash on the host.

I originally tried to implement a full

append_signature_bytes(pczt_bytes, input_index, sig)

helper, but it quickly became clear that:

  • Doing this correctly would require upstream API changes, not just local glue code.
  • Anything “clever” (e.g., trying to reconstruct keys or poke private fields via hacks) would be fragile and unsafe.

How I handled it:

  • Implemented and exposed a robust

    get_sighash_bytes

    helper in Rust, wired through the CLI and TS as

    getSighash

    , using:
    • Pczt::into_effects

      and
    • zcash_primitives::transaction::sighash_v5::v5_signature_hash

      (ZIP‑244).
  • Left

    append_signature_bytes

    as an explicit stub that always returns a clear error, and documented the blocker in

    MVP.md

    ,

    SUBMISSION.md

    , and the roadmap.
  • Surfaced this honestly in the TS API as

    appendSignature

    , which currently reports the same documented limitation.

This was a design decision: better to be crystal clear about what’s possible with today’s

pczt

than to pretend we have full signer support.

2. Getting the builder + PCZT roles to cooperate (ZIP‑317, Orchard, change)

Another challenge was wiring

zcash_primitives::transaction::builder::Builder::build_for_pczt

and the

pczt

Creator/Prover/Extractor roles together in a way that:

  • Respects ZIP‑317 fees.
  • Keeps behavior simple and predictable for an MVP (single recipient, at most one transparent change output).
  • Still produces a genuine PCZT object that the upstream Prover and Extractor are happy with.

This required:

  • Deriving a canonical P2PKH script from the provided

    pubkey

    instead of trusting the caller’s

    script_pubkey

    .
  • Letting the builder compute the ZIP‑317 fee and only creating transparent change if

    sum(inputs) > recipient + fee

    , always to the first input’s P2PKH address.
  • Implementing

    verify_before_signing

    to:
    • Inspect the PCZT’s transparent and Orchard bundles.
    • Re‑derive input/recipient/change totals.
    • Enforce

      inputs = recipients + transparent change + fee

      and reject mismatches.

How I handled it:

  • Leaned heavily on the canonical builder API (

    build_for_pczt

    ,

    get_fee

    ,

    add_orchard_output

    , etc.), rather than hand‑rolling bundles.
  • Wrote targeted Rust tests that:
    • Check transparent input/output mapping.
    • Check Orchard recipient mapping and proving.
    • Check value‑conservation and change invariants.
  • On the TS side, made

    estimateFeeAndChange

    an explicit “best‑effort helper”, with Rust

    verify_before_signing

    as the source of truth.

3. Dependency and feature‑flag juggling

Getting a consistent set of crate versions and features (for

pczt

,

zcash_primitives

,

zcash_protocol

,

orchard

,

zcash_address

,

zcash_keys

,

zcash_transparent

, etc.) that all compile together with:

  • pczt

    features:

    prover

    ,

    zcp-builder

    ,

    transparent

    ,

    orchard

    ,

    sapling

    ,

    tx-extractor

    ,

    signer

    .
  • zcash_primitives

    features like

    transparent-inputs

    .

…was non‑trivial. There were several false starts with version mismatches and conflicting feature sets.

How I handled it:

  • Iterated on

    Cargo.toml

    until all crates agreed on versions/feature sets that support:
    • Building PCZT from the builder.
    • Orchard proving.
    • ZIP‑244 sighash.
    • Transaction extraction.
  • Locked this into

    rust-core/Cargo.toml

    and documented the behavior in

    MVP.md

    /

    pcztkit-notes.md

    so future contributors don’t have to rediscover the matrix.

Tracks Applied (3)

Privacy Infrastructure & Developer Tools

**Directly centered on Zcash and ECC’s ecosystem Electric Coin Company Fits Perfectly**: It’s built around ECC‑maintain...Read More

Electric Coin Company

Privacy Infrastructure & Developer Tools

PCZTKit is also a strong fit for the Zcash Community Grants track because it is explicitly designed as reusable infrastr...Read More

Zcash Community Grants

General Bounty

For a general bounty track, PCZTKit stands out as a practical, shippable developer tool rather than a purely conceptual ...Read More

Project Tachyon

Discussion

Builders also viewed

See more projects on Devfolio