Client-only one-page app for generating Zcash gift cards.
The app:
- generates a fresh 32-byte secret in the browser
- derives a temporary Sapling address from that secret via Rust/WASM
- builds a ZIP 321 funding URI
- builds a ZIP 324 claim URI
- renders QR codes locally
- lets the sender copy, share, or download a backup of the claim URI
No backend is used for key generation, claim URI generation, or secret storage.
Implemented:
- three-screen flow: create, fund, ready
- amount validation
- browser RNG via
crypto.getRandomValues - Sapling temporary address derivation in Rust/WASM
- ZIP 321 funding URI generation
- ZIP 324 claim URI generation
- QR rendering
- copy/share/download backup actions
Not implemented:
- chain-based funding verification
- local encrypted draft persistence
- deterministic recovery from a wallet seed
- Vite
- vanilla JavaScript
- Rust
wasm-packsapling-cryptozcash_protocolqrcode
src/
app.js
main.js
state.js
styles.css
ui/
createForm.js
fundingScreen.js
readyScreen.js
crypto/
backup.js
random.js
uri321.js
uri324.js
zcashWasm.js
qr/
renderQr.js
wasm/
pkg/
wasm/
zcash-gift-wasm/
Cargo.toml
src/lib.rs
- Node.js 22+ recommended
- Rust toolchain
wasm32-unknown-unknowntargetwasm-packwasm-bindgen-cli
If needed:
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
cargo install wasm-bindgen-clinpm.cmd installBuild the WASM package and start the dev server:
npm.cmd run devOpen the local URL printed by Vite, usually:
http://localhost:5173
The generated WASM bindings in src/wasm/pkg are intentionally not committed.
After cloning, run one of these before expecting the app to load:
npm.cmd run devor:
npm.cmd run build:wasmnpm.cmd run buildPreview the production build:
npm.cmd run previewUsually available at:
http://localhost:4173
This repo is configured to deploy to:
https://zcashme.github.io/zip324wasm/
Deployment model:
- GitHub Pages
- source: GitHub Actions
- output:
dist/
To enable it in GitHub:
- Open the repository settings.
- Go to
Pages. - Set
Build and deploymenttoGitHub Actions.
The workflow will:
- install Node
- install Rust
- add
wasm32-unknown-unknown - install
wasm-pack - install
wasm-bindgen-cli - run
npm ci - run
npm run build - publish
dist/to GitHub Pages
- Enter amount and optional memo.
- Click
Create gift card. - The app generates a random secret locally.
- The app derives a temporary Sapling address locally.
- The app displays the funding address, ZIP 321 URI, and QR code.
- Fund that address with the intended amount.
- Click
I sent the funds. - The app generates the ZIP 324 claim URI locally.
- Copy, share, or download the backup.
- Secrets are generated in-browser with
crypto.getRandomValues. - The gift card secret is kept in memory only.
- No analytics or backend secret storage are used.
- The claim URI is bearer access to the funds.
- Anyone with the claim URI can claim the funds.
- Refreshing the tab before backup can lose access to the gift card.
- Funding is user-confirmed only. The app does not verify the chain.
- Wallet support for ZIP 324 handling may vary.
- This prototype assumes the funded amount and claim amount stay consistent.
Implemented in the frontend or WASM bridge:
generateKeyBytes()deriveTemporaryAddressFromKey(keyBytes, network)encodeZip324KeyBech32(keyBytes, network)buildFundingUri321(address, amountZec, memo)buildClaimUri324(amountZec, memo, bech32Key)downloadBackupText(data)copyText(text)shareText(text)
- The generated WASM package is written to
src/wasm/pkg. - The app imports the generated WASM bindings from source so Vite dev/build work consistently.
- The page CSP allows local WebAssembly compilation with
'wasm-unsafe-eval'.
Basic check:
- Run
npm.cmd run dev. - Create a gift card with a valid amount such as
1.23. - Confirm that a Sapling address and funding QR appear.
- Click
I sent the funds. - Confirm that a ZIP 324 URI, claim QR, and backup download action appear.