Status: Draft
Date: 2026-04-01
Depends on: Zcash Shielded Assets (ZIP 226, ZIP 227) protocol activation
When Zcash Shielded Assets ship, asset issuers will need a way to attest properties of their assets on-chain without revealing holder identities or transaction details. ZAP1 provides this layer: structured attestation events committed to a Merkle tree and anchored to Zcash via shielded memos.
This document defines ZAP1 event types for the ZSA lifecycle, extending the registry into the 0x10-0x1F range. These types are reserved and will activate when the ZSA protocol is deployed on mainnet.
Grounding: all types below map directly to ZIP 226/227 consensus objects. AssetId = (issuer, assetDescHash) per ZIP 227. Issuance is transparent and issuer-authorized. Burn is the provable supply-reduction mechanism (ZIP 226). Key rotation is not supported - compromise handling is "finalize old issuer, move to new issuer" (ZIP 227).
| Type | Name | Payload focus | ZIP reference |
|---|---|---|---|
0x10 |
ZSA_ISSUER_BIND |
H(issuer || issuer_meta_root) |
ZIP 227 issuer registration |
0x11 |
ZSA_ASSET_BIND |
H(issuer || assetDescHash) |
ZIP 227 AssetId derivation |
0x12 |
ZSA_REFERENCE_NOTE |
H(issuer || assetDescHash || txid || output_index) |
ZIP 227 reference note |
0x13 |
ZSA_ISSUE |
H(issuer || assetDescHash || amount_be || txid || issue_action_index) |
ZIP 227 issuance action |
0x14 |
ZSA_FINALIZE |
H(issuer || assetDescHash || txid) |
ZIP 227 finalization |
0x15 |
ZSA_ZERO_ISSUE_REVOKE |
H(issuer || assetDescHash || txid || reason_code) |
ZIP 227 zero-value revocation |
0x16 |
ZSA_BURN |
H(asset_base || amount_be || txid) |
ZIP 226 burn mechanism |
0x17 |
ZSA_SUPPLY_SNAPSHOT |
H(issuer || assetDescHash || supply_be || final_flag) |
ZIP 227 global state |
| Type | Name | Payload focus | Use case |
|---|---|---|---|
0x18 |
ZSA_RESERVE_LOCK |
H(issuer || assetDescHash || ext_chain_id || ext_txid_hash || amount_be) |
Cross-chain reserve lock |
0x19 |
ZSA_RESERVE_RELEASE |
H(issuer || assetDescHash || ext_chain_id || ext_txid_hash || amount_be) |
Cross-chain reserve release |
0x1A |
ZSA_REDEEM_REQUEST |
H(issuer || assetDescHash || burn_txid || amount_be || destination_hash) |
Burn-and-redeem request |
0x1B |
ZSA_REDEEM_COMPLETE |
H(issuer || assetDescHash || burn_txid || settlement_txid_hash) |
Redemption settlement |
0x1C |
ZSA_COMPROMISE_NOTICE |
H(issuer || incident_id_hash || timestamp_be) |
Issuer key compromise |
0x1D |
ZSA_SUCCESSOR_ISSUER |
H(old_issuer || new_issuer || assetDescHash) |
Key rotation via new issuer |
0x1E |
ZSA_METADATA_BIND |
H(issuer || assetDescHash || metadata_root) |
Metadata commitment |
0x1F |
ZSA_AUDIT_ROOT |
H(issuer || audit_root || period_be) |
Periodic audit attestation |
All hashes use BLAKE2b-256 with NordicShield_ personalization, consistent with the base profile. All variable-length fields are length-prefixed with 2-byte big-endian length. Integer fields use big-endian encoding (8 bytes for amounts, 4 bytes for indices and reason codes).
ZSA_ISSUER_BIND = BLAKE2b_32(0x10 || len(issuer) || issuer || issuer_meta_root)
ZSA_ASSET_BIND = BLAKE2b_32(0x11 || len(issuer) || issuer || assetDescHash)
ZSA_REFERENCE_NOTE = BLAKE2b_32(0x12 || len(issuer) || issuer || assetDescHash || txid || output_index_be)
ZSA_ISSUE = BLAKE2b_32(0x13 || len(issuer) || issuer || assetDescHash || amount_be || txid || issue_action_index_be)
ZSA_FINALIZE = BLAKE2b_32(0x14 || len(issuer) || issuer || assetDescHash || txid)
ZSA_ZERO_ISSUE_REVOKE = BLAKE2b_32(0x15 || len(issuer) || issuer || assetDescHash || txid || reason_code_be)
ZSA_BURN = BLAKE2b_32(0x16 || asset_base || amount_be || txid)
ZSA_SUPPLY_SNAPSHOT = BLAKE2b_32(0x17 || len(issuer) || issuer || assetDescHash || supply_be || final_flag)
ZSA_RESERVE_LOCK = BLAKE2b_32(0x18 || len(issuer) || issuer || assetDescHash || ext_chain_id_be || ext_txid_hash || amount_be)
ZSA_RESERVE_RELEASE = BLAKE2b_32(0x19 || len(issuer) || issuer || assetDescHash || ext_chain_id_be || ext_txid_hash || amount_be)
ZSA_REDEEM_REQUEST = BLAKE2b_32(0x1A || len(issuer) || issuer || assetDescHash || burn_txid || amount_be || destination_hash)
ZSA_REDEEM_COMPLETE = BLAKE2b_32(0x1B || len(issuer) || issuer || assetDescHash || burn_txid || settlement_txid_hash)
ZSA_COMPROMISE_NOTICE = BLAKE2b_32(0x1C || len(issuer) || issuer || incident_id_hash || timestamp_be)
ZSA_SUCCESSOR_ISSUER = BLAKE2b_32(0x1D || len(old_issuer) || old_issuer || len(new_issuer) || new_issuer || assetDescHash)
ZSA_METADATA_BIND = BLAKE2b_32(0x1E || len(issuer) || issuer || assetDescHash || metadata_root)
ZSA_AUDIT_ROOT = BLAKE2b_32(0x1F || len(issuer) || issuer || audit_root || period_be)
Fields:
issuer- the issuance validating key encoding (per ZIP 227)assetDescHash- 32-byte hash of the asset description (per ZIP 227 AssetId derivation)asset_base- the asset base point for burn operations (per ZIP 226)amount_be- 8-byte big-endian amount in base unitstxid- 32-byte transaction identifieroutput_index_be/issue_action_index_be- 4-byte big-endian indexext_chain_id_be- 4-byte big-endian external chain identifierext_txid_hash- 32-byte hash of the external chain transactiondestination_hash- 32-byte hash of the redemption destinationsettlement_txid_hash- 32-byte hash of the settlement transactionincident_id_hash- 32-byte hash of the compromise incident recordissuer_meta_root/metadata_root/audit_root- 32-byte Merkle roots of metadata or audit treesreason_code_be- 4-byte big-endian reason codefinal_flag- 1-byte (0x00 = not finalized, 0x01 = finalized)period_be- 4-byte big-endian audit period identifier
An institutional issuer creates a ZSA and commits every issuance and burn to ZAP1. The supply snapshot at any point is provable from the Merkle tree. Auditors verify the supply trail from the proof path without seeing individual holder balances.
A cross-chain bridge locks collateral on an external chain and mints ZSA tokens on Zcash. Each lock and release is attested via ZSA_RESERVE_LOCK and ZSA_RESERVE_RELEASE. The Solidity verifier (zap1-verify-sol) can verify these attestations on the external chain, creating a two-way audit surface.
ZIP 227 does not support key rotation. If an issuer key is compromised, the response is to finalize the old asset and issue under a new key. ZSA_COMPROMISE_NOTICE and ZSA_SUCCESSOR_ISSUER create a permanent, verifiable record of this transition in the Merkle tree.
Using zap1-verify-sol, an EVM smart contract can verify that a ZSA was issued, that its supply matches a snapshot, or that a bridge reserve was locked - all without a custodian, a guardian set, or exposing the Zcash transaction graph.
ZAP1 ZSA events are application-layer attestations, not consensus-layer operations. They do not modify ZSA issuance or transfer mechanics. They sit above the asset protocol and provide a verifiable audit surface.
The ZSA protocol handles: issuance, transfer, burn at the consensus layer.
ZAP1 handles: attestation, supply proof, compliance, audit at the application layer.
These event types will activate when:
- ZSA protocol is deployed on Zcash mainnet
- The
AssetIdandissuerkey formats are finalized in ZIP 226/227 - Hash constructions are validated against the ZSA issuance key derivation
- Test vectors are published matching live ZSA issuance transactions
Until then, types 0x10-0x1F are reserved in the ZAP1 registry. The API will reject events in this range.