Private reward distribution on Avalanche.
ZUS lets a campaign creator fund a reward drop without exposing the recipient list onchain. A claimant proves eligibility with a zk proof, the circuit derives a one-time stealth address, and ZusProtocol pays that stealth address.
- The Rust API in
userslist/creates a campaign and builds the Merkle tree. - The protocol stores the campaign root, verifier, message, and payout onchain.
- The TUI fetches the claimant's Merkle path from the API.
- Noir + Barretenberg generate a proof and public inputs.
ZusProtocolverifies the proof and pays the derived stealth address.
The TUI in tui/ is the offchain tool that drives the claim flow. It uses encrypted Foundry keystores plus cast, nargo, and bb to:
- resolve the wallet
- fetch claim inputs from the Rust API
- write
Prover.toml - generate the witness and proof
- call
previewClaim(...) - send
claim(...)toZusProtocol
For the current MVP, the TUI uses fixed demo values for message and stealth_tweak.
zus_addy/- Noir circuit for Merkle membership, nullifier derivation, and stealth address derivationuserslist/- Rust API for campaign creation and claim payloadstui/- Rust terminal app for proof generation and claimingverifier/- UltraHonk Solidity verifierzusprotocol/- protocol contract for campaigns, funding, and claimsfrontend/- React frontend
ZusProtocol.solmanages campaigns, verifies claims, prevents nullifier reuse, and sends payoutsHonkVerifier.solverifies the UltraHonk proof generated from the Noir circuit
- Verifier: 0x2Ab7e6Bc7A69d0D37B43ea2f7374a12aC3f04CAB
- ZusProtocol: 0x19b2d6A4D21078A215406eeF1F71731AEE84F7b4
- Merkle paths stay offchain; only the root is committed onchain
- payouts are currently flat per campaign
- the shared verifier is reused across campaigns
See also:
verifier/README.mdzusprotocol/README.md
