Reference
Exact values, byte layouts and endpoint shapes. Descriptive only — read from source at zkas-v1.0.9, with live figures marked.
Addresses, keys and units
- Address encoding
- bech32; HRP
zkas:mainnet ·zkastest:testnet ·zkasdev:devnet ·zkassim:simnet - Version byte
Version::ShieldedOrchard = 9- Raw payload
- 43 bytes = diversifier(11) ‖ pk_d(32)
- Script class
ShieldedOrchard => ScriptClass::NonStandard— a 43-byte script is a raw Orchard recipient- Key tree
- 32-byte seed → spending key → FVK → IVK, external scope only
- Diversified addresses
- supported by Orchard, but no shipped component derives index > 0. One wallet, one address
- Mnemonics
- none. No BIP-39 anywhere. Key material is a raw 32-byte seed, hex-encoded
- Units
SOMPI_PER_ZKAS = 100_000_000— 8 decimals
What each key reveals. The IVK decrypts every incoming note of that seed, irrevocably once shared. The OVK, if attached at send, makes outgoing details recoverable by you. Message signing proves address control without spending but discloses the FVK — use a dedicated seed for it.
Emission and supply
The dev fee is skimmed from the subsidy, never from transaction fees — value is conserved, and it is appended as one extra coinbase output, paid every 1000 blocks as a shielded note.
| Age | Cumulative supply | Age | Cumulative supply |
|---|---|---|---|
| 3 months | ~351M | 3 years | ~873M |
| 6 months | ~527M | 5 years | ~911M |
| 1 year | ~665M | 10 years | ~1.005B |
| 2 years | ~854M | 20 years | ~1.195B |
Long-run inflation is ~2.2% at tail onset (~year 2), decaying toward ~1% and below over decades. Emission is heavily front-loaded: roughly half of all curve issuance happens in the first quarter. The tail is perpetual by design — it funds miner security forever rather than relying on fee revenue.
Consensus parameters
| Parameter | Value |
|---|---|
target_time_per_block | 1000 ms (1 BPS) |
| Coinbase maturity | 100 blocks (COINBASE_MATURITY_SECONDS = 100) |
shielded_anchor_depth | 600 × BPS — ~10 min; minimum anchor maturity before a note is spendable |
max_shielded_anchor_age | pruning_depth / 4 — ~7.5 h; older anchors are dropped |
| Standard transaction mass cap | 500,000 transient mass at 4 per byte ⇒ bundle ≤ 124,744 bytes |
| Storage mass | zero for shielded transactions |
| Subnetwork lanes | 50 per block, gas 1e9 per lane; selection freezes once full |
| Difficulty window | 2641 s ÷ 4 = 661 samples (min 150) ⇒ retarget over ~44 min |
| Dev fee | ZKAS_DEV_FEE_PERMILLE = 50 (5%), consensus-enforced, payout interval 1000 blocks |
| Activations | merged mining + Toccata from genesis; multi-producer anchors and dev-fee accrual at DAA 757,000 |
| Wire identity | genesis hash is the network_domain in every sighash — a signature from another chain can never replay here |
Transaction format and sighash
A shielded transaction is version 2, non-coinbase, with no transparent inputs or outputs. The Orchard bundle travels in the transaction payload.
| Quantity | Value |
|---|---|
| Max actions per bundle | 512 (MAX_ACTIONS_PER_BUNDLE); 38 in a standard transaction |
| Per-action wire size | 884 B — nullifier 32, rk 32, cmx 32, cv_net 32, epk 32, enc_ciphertext 580, out_ciphertext 80, spend_auth_sig 64 |
| Bundle header | 117 B |
| Proof size | 2720 + 2272·n |
| Total bundle | wire_len(n) = 2837 + 3156·n |
enc_ciphertext (580 B) | version, diversifier 11, value 8, rseed 32, memo 512, tag 16 — read with the IVK |
out_ciphertext (80 B) | recoverable only if an OVK was attached at send |
| Burn flag | BUNDLE_FLAG_BURN = 0b100 gates a trailing value(8) ‖ kaspa_recipient(32) |
What the signatures commit to
sighash = BLAKE2b(
network_domain (genesis hash, 32B) # binds to this chain
‖ flags ‖ value_balance ‖ anchor ‖ action_count
‖ for each action:
nullifier ‖ rk ‖ cmx ‖ cv_net ‖ epk
‖ enc_ciphertext ‖ out_ciphertext # <- the memo IS committed
# spend_auth_sig excluded: it signs this digest
‖ len(tx_context) ‖ tx_context )
tx_context = version ‖ subnetwork_id ‖ lock_time ‖ gas # 38 bytesenc_ciphertext is inside the digest, memo bytes are already authenticated by both the spend-auth and binding signatures — which is what makes publicly indexable app data possible with no consensus change.Timelocks. The transaction-level absolute lock_time is real and bound into the sighash, so coarse timeouts work. A per-note relative timelock does not exist and is not a small change — it needs a new note field, a changed note commitment, a new circuit public input and regenerated keys.
Fees are byte-proportional, therefore per-action: 1–2 actions = 0.0186 ZKAS, 38 actions = 0.2458 ZKAS. A flat fee below the node minimum is rejected by relay; the wallet raises it automatically.
Node RPC (shielded)
gRPC. Four shielded methods on top of the inherited Kaspa surface — together the whole trustless client interface: mirror the tree, place a birthday, audit supply, read mining income.
GetShieldedBlocks- Request
startHash(exclusive chain-block cursor),limit,metadataOnlyResponseblocks[]— per chain block:hash,blueScore,daaScore,timestamp,coinbaseTxid,coinbaseOutputs[](scriptPublicKey,value,commitment),acceptedActions[]with parallelacceptedTxids[]in applied order; plusreorged(cursor orphaned → rescan) andsinkBlueScore GetShieldedTreeState- Request—Response
blockHash,daaScore,size,leaf,ommers[],historyFromDaaScore,historyComplete GetShieldedSupply- Request—Response
cumulativeCoinbase,cumulativeFees,cumulativeBurns,poolValue,noteCount, as of a named chain block GetShieldedCoinbaseRewards- Request
recipients[](43-byte hex),startHash,limit= blocks scannedResponserewards[]keyed by(coinbaseTxid, outputIndex), plusscannedBlocks,nextCursor
reorged.historyFromDaaScore is the oldest height this node can serve; a wallet whose birthday is below it cannot be fully scanned here and any balance it reports is a lower bound. historyComplete says the node can enumerate to genesis. Check both before showing a number to a user.Coinbase mints are public: recipient and value are visible with no viewing key. A block's coinbase pays its mergeset, not its finder.
Public REST API
Unauthenticated, read-only, over the live mainnet node. Base https://explorer.zkas.info/api. Useful for dashboards, indexers and monitoring without running anything.
/info/blockdagblockCount,headerCount,difficulty,networkName,tipHashes,pruningPointHash,pastMedianTime/info/pulse- measured block rate:
bps15m,averageBlockTime15m,blocks15m, 15-secondblockBinsanddifficultyBins /info/shielded- turnstile and tree:
anchor,noteCount,nullifierCount,turnstileIn,turnstileOut,emissionPerBlock /info/coinsupplycirculatingSupplyin sompi,emissionModel,maxSupply(null)/info/blockreward·/info/halving- current subsidy; next reduction amount, DAA score, ETA, percent
/info/miners- per-pool attribution over the last hour:
blocks,share, derivedhashrate,source,distinctAddresses /info/merged-mining- peers observed merge-mining, with the Kaspa node each is paired to
/info/network·/info/nodes·/info/relay- peer counts, masked peer subnets, relay credit
/info/work-history·/info/reorgs- difficulty over time; observed reorg depths
/info/fee-estimatepriorityBucket/normalBuckets/lowBucketswith feerate and ETA/blocks/recent·/blocks/:id- recent blocks with coinbase outputs; one block by hash
/transactions/:id·/transactions/search·/transactions/count- transaction lookup
/health- liveness
B=https://explorer.zkas.info/api
# tip, difficulty, pruning point
curl -s $B/info/blockdag
# network hashrate in PH/s, from MEASURED bps
D=$(curl -s $B/info/blockdag | jq .difficulty)
P=$(curl -s $B/info/pulse | jq .bps15m)
echo "$D $P" | awk '{printf "%.1f PH/s\n", 2*$1*$2/1e15}'
# shielded pool, tree size, current anchor
curl -s $B/info/shielded
# who mined the last hour, with hashrate share
curl -s $B/info/miners | jq '.miners[] | {label:.key, blocks, share}'difficulty with the measured bps15m: hashrate = 2 × difficulty × BPS. The chain does not run at exactly 1.000 blocks/second.GET /info/shielded
{ "anchor": "3d8ee39caf1cd16dd8dd…88bd6fc9",
"blueScore": "5512774", "emissionPerBlock": 37.79763149,
"noteCount": 5752657, "nullifierCount": 139889,
"turnstileIn": "27029204918145767", "turnstileOut": "0" }
GET /info/pulse
{ "bps15m": 0.9777777777777777, "blocks15m": 880,
"averageBlockTime15m": 1.0227272727272727, "binSeconds": 15 }
GET /info/halving
{ "currentAmount": 37.79763149, "nextHalvingAmount": 35.67621345,
"reductionPercent": 5.61, "blocksRemaining": 387168,
"nextHalvingDate": "in ~4 days", "atTailFloor": false }Live, September 2026. turnstileOut is 0 and maxSupply is null: nothing has ever left the pool, and emission has a perpetual tail rather than a cap.
Address routes (/addresses/:address/…) are inherited from the Kaspa explorer API and are not meaningful on mainnet — there are no transparent addresses to query.
Wallet API
The full zkas-walletd HTTP surface, its flags, the /api/status field semantics and the payout sizing model live on their own page: Wallets & walletd → endpoints.
Versions and compatibility
| Component | Current | Notes |
|---|---|---|
| Node + walletd | zkas-v1.0.9 | one release carries linux-amd64, osx-arm64, osx-x86_64 and win64 builds |
| Transaction version | 2 shielded | 1 is the Toccata baseline; the shielded bundle only exists on version 2 |
| P2P default (≤ v1.0.7) | 16111 listen | collided with Kaspa; current releases listen on 16811 |
/api/status fields — spend_ready, blocks_behind, warming, loading, missing_history, the pending_* pair — are serde(default), so an older daemon simply omits them. Treat absent as the safe value and never hard-fail on an unknown field.Glossary
- note
- a shielded output holding a value and an owner; the unit of value, and the unit that costs proving time
- cmx
- note commitment — the tree leaf; hides the note's contents
- nullifier
- published when a note is spent; prevents double-spending without revealing which note
- anchor
- a past commitment-tree root a spend proves membership against
- frontier
- the ~32-node summary of the tree a node keeps instead of the whole tree
- witness
- the Merkle path from a note to an anchor. Wallets hold their own
- FVK / IVK / OVK
- full / incoming / outgoing viewing key
- turnstile
- the public integer ledger bounding value entering and leaving the pool
- mergeset
- the parallel DAG blocks a chain block merges — and what its coinbase pays
- aux-PoW
- merged mining: proving work for this chain via a parent chain's block header
- sompi
- the base unit; 108 sompi = 1 ZKAS
- transient mass
- Kaspa's size-based transaction cost, 4 per byte, capped at 500,000 for a standard transaction
- Toccata
- the Kaspa upgrade that shipped covenants and zk verification opcodes — see the Kaspa interface
zkas-rusty at zkas-v1.0.9. Byte layouts, endpoint shapes and parameters are read from source; figures marked measured come from the live mainnet node. If this page contradicts the code, the code is right — report it.