Skip to content
home/guides/rust & python

Native SDKs.

The same three operations as the TypeScript client — shield, attest, track — from a backend service. Both clients talk directly to the public Nitro RPC and a public lightwalletd; neither one calls a Z-Relay server.

Which client should you use?
Run the TypeScript SDK in the browser, so proving stays on the user's device. Reach for Rust or Python when the work is server-side — a payout scheduler, a treasury service, an indexer — and you control the host holding the keys.

Installation

Cargo.toml
# Cargo.toml
[dependencies]
zrelay-sdk = "0.1"
tokio = { version = "1", features = ["full"] }
alloy = { version = "0.8", features = ["signer-local"] }
Rust crate
zrelay-sdk · MSRV 1.79 · tokio runtime
Python package
zrelay · 3.10+ · asyncio native
Signing
Rust: alloy · Python: eth-account
Proving
arkworks (Rust) · bundled wheel (Python)
Network calls
Public RPC + public lightwalletd only
Blocking API
Python exposes sync wrappers under zrelay.sync

Shield

Moves an EVM asset into a Zcash shielded note. Handles the approval, the DEX route, the escrow deposit and the gas surcharge, then resolves once the Orchard output lands in a finalised block.

main.rs
1use zrelay_sdk::{Client, ShieldParams, Pool};2use alloy::signers::local::PrivateKeySigner;3 4#[tokio::main]5async fn main() -> anyhow::Result<()> {6    let signer: PrivateKeySigner = std::env::var("PRIVATE_KEY")?.parse()?;7 8    let client = Client::builder()9        .chain_id(4663)                 // Robinhood Chain mainnet10        .signer(signer)11        .build()?;12 13    // One call: swap, escrow, threshold-sign, mint into Orchard.14    let settlement = client15        .shield(ShieldParams {16            token: "0xUSDG_ADDRESS".parse()?,17            amount: 2_500_000_000u128,          // 2,500 USDG (6 decimals)18            recipient_z_address: "u1xyz...orchard_unified_address".into(),19            memo: Some("Quarterly stock-token yield".into()),20            pool: Pool::Orchard,21            ..Default::default()22        })23        .await?;24 25    println!("{}", settlement.zcash_tx_id);26    println!("{:?}", settlement.status);        // Status::Settled27    Ok(())28}

Attest

Builds a Groth16 proof about shielded holdings. On a server the viewing key is read from your secret store into process memory and never transmitted — but it is on a machine you operate, which is a materially different risk profile from the browser prover.

attest.rs
1use zrelay_sdk::{Client, AttestParams, Condition};2 3// The witness is built in-process. The viewing key never leaves the host.4let attestation = client5    .attest(AttestParams {6        viewing_key: viewing_key,           // read from your secret store7        condition: Condition::MinZecBalance {8            amount: "100.0".into(),9            at_block_height: Some(2_654_900),10        },11        destination_contract: vault_address,12    })13    .await?;14 15// Groth16 proof, ready to submit from your own signer.16println!("{:?}", attestation.claim_hash);17println!("{} bytes", attestation.proof.len());   // ~192
Server-side proving moves the risk, it does not remove it
A compromised host with a full viewing key in memory discloses the whole wallet history. Prefer an incoming viewing key (ivk) where the predicate only concerns received value, scope the secret to a single process, and zero it as soon as the proof exists.

Track

Both clients expose the settlement stages as a stream, so a worker can record progress instead of polling.

track.rs
use futures::StreamExt;

let mut stream = client.track(&settlement.tracking_id).await?;

while let Some(update) = stream.next().await {
    let update = update?;
    println!("{:?} at {}", update.stage, update.at);
    // Stage::EvmSequenced | FrostSigned | OrchardIncluded | Settled
}

Errors

Failures are typed. The variants match the TypeScript ZRelayError.code values one-for-one, so a service that already handles the TS errors needs no new logic.

errors.rs
1use zrelay_sdk::Error;2 3match client.shield(params).await {4    Ok(settlement) => println!("{}", settlement.zcash_tx_id),5    Err(Error::QuoteExpired { quote_id, .. })       => { /* re-quote */ }6    Err(Error::TransparentReceiverOnly { .. })      => { /* bad z-addr */ }7    Err(Error::InsufficientGasCoverage { .. })      => { /* attach surcharge */ }8    Err(Error::SlippageExceeded { .. })             => { /* route moved */ }9    Err(Error::ThresholdNotMet { tracking_id })     => { /* reclaim later */ }10    Err(e) => return Err(e.into()),11}

Feature parity

shield / quote / track
Rust ✓ · Python ✓
attest (server-side proving)
Rust ✓ · Python ✓
verify_attestation
Rust ✓ · Python ✓
Browser / WASM proving
TypeScript only
React bindings
TypeScript only

Related