ZOLK writes its record to the Zcash chain as shielded memos from one wallet account, the registry, whose memos are listed on the transparency page. Every memo is one line of UTF-8 text, at most 512 bytes, starting with the tag ZOLK/1. Six kinds exist.
Lines
ZOLK/1 card #0042 owner=<16 hex> art=<64 hex> at=<ISO time> ZOLK/1 points <day> root=<64 hex> members=<n> total=<zpoints> ZOLK/1 seed <day> commit=<64 hex> ZOLK/1 reveal <day> seed=<64 hex> block=<height>:<64 hex block hash> ZOLK/1 art <collection> supply=<n> root=<64 hex> ZOLK/1 code <40 hex git commit> at=<ISO time>
Hashes
- owner = sha256("zolk:owner:" + X user id), first 16 hex characters. A member's own tag is shown on their profile. It never changes; a username can.
- art = sha256 of the card's 48×48 pixel grid, rows joined with a newline, cells joined with a comma. The grid is drawn by the collection's engine from the seed
<collection>:<card number>. - points root = Merkle root over leaves sha256(owner + ":" + zpoints), leaves in owner order; pairs hashed as sha256(left bytes ‖ right bytes), an odd last leaf paired with itself. The leaves of each day are in the day's JSON at
/api/v1/transparency/day/<day>. - seed commit = sha256("zolk:seed:" + seed). The seed is 32 random bytes we draw the day before, as hex.
- art root = Merkle root (same rule) over the art hashes of every card number from 1 to the supply.
How a day's chests are dealt
- The day before, we draw a seed and write its commitment. We cannot change the seed afterwards without the commitment failing.
- The day's first block is the lowest block whose timestamp is at or after midnight UTC. Block times may step backwards slightly, so the search also reads the twenty-four blocks below the first candidate and takes the lowest one at or after midnight, repeating until none is. The block must be two deep before it counts.
- digest = sha256("zolk:day:" + seed + ":" + block hash in lower-case display order).
- A member's chests that day are drawn one by one with mulberry32 seeded by fnv1a32(sha256("zolk:kinds:" + digest + ":" + owner tag)): for chest i take the next number u in [0, 1); it is a card chest if u is below the card chance, a ZOLK chest if below the card chance plus the ZOLK chance, empty otherwise. The chances and the number of chests per member are the day's
mix, published with the reveal. - A card chest pays a whitelist card to a member who does not hold one yet and who has opened a chest on an earlier night; on a first night, or for a member who already holds a card, it pays its ZOLK instead. The reveal memo after the day carries the seed and the block, so anyone can redo steps 3 and 4 for every member who opened a chest.
Reading the memos
zcash-devtool wallet -w ./zolk-registry init-fvk --name zolk -n main -s zecrocks --fvk <viewing key> --birthday <height> zcash-devtool wallet -w ./zolk-registry sync -s zecrocks zcash-devtool wallet -w ./zolk-registry enhance -s zecrocks zcash-devtool wallet -w ./zolk-registry list-tx
Any wallet that imports a unified full viewing key works the same way. The registry's funds are operating money for memo fees, topped up from the project's hot account; members' ZEC is never in it. A whitelist card is a place on the list for the ZOLK collection, not the collectible itself. The ZOLK token is held as a balance here, anchored daily by its root, until it is issued as a Zcash Shielded Asset.