Skip to content

feat(wallet): wallet-server HTTP API and web dapp#1

Closed
saroupille wants to merge 6 commits intotrilitech:mainfrom
saroupille:feat/wallet-server
Closed

feat(wallet): wallet-server HTTP API and web dapp#1
saroupille wants to merge 6 commits intotrilitech:mainfrom
saroupille:feat/wallet-server

Conversation

@saroupille
Copy link
Copy Markdown

@saroupille saroupille commented Apr 17, 2026

Summary

  • Adds `wallet-server`: an Axum HTTP server that exposes a private TzEL wallet over a local REST API (shield/transfer/unshield/scan/balance/address)
  • Adds `web/`: a Vite + React dapp connecting to `wallet-server` in real mode, with a mock mode for UI development without a live node
  • Adds bech32 encoding for `PaymentAddress`, derives `PartialEq`/`Eq` on it, and removes unused `verify_wots_signature_against_leaf`
  • Adds `docs/wallet_server.md` with a step-by-step manual testing guide

Architecture

```
web dapp (React) → wallet-server (Axum) → tzel-operator (ledger API)
→ proving-service (optional STARK proofs)
```

Proof modes

  • `--skip-proof`: skips STARK proofs, for demos and integration testing
  • `--proving-service=`: delegates proof generation to a remote HTTP proving service

API endpoints

Method Path Description
`GET` `/balance` Returns `{"private_balance": N}`
`POST` `/address` Returns a PaymentAddress JSON (advances `addr_counter`, persists wallet)
`POST` `/scan` Pulls new notes from ledger, removes spent ones
`POST` `/shield` Shields L1 funds into the private pool
`POST` `/transfer` Transfers between shielded addresses
`POST` `/unshield` Withdraws to a public L1 address

Design decisions

  • Wallet locking: state is held in `Arc<Mutex<(WalletFile, ...)>>` — all handlers hold the lock for their full duration (no partial updates)
  • Save ordering: `save_wallet` is called only after a successful ledger POST, so a network failure does not consume an address slot or mark notes as spent
  • `/address` is POST, not GET: because it mutates `addr_counter` and persists to disk
  • 502 on ledger errors: all ledger-unreachable errors return `502 BAD_GATEWAY`, not `400`

Test plan

See docs/wallet_server.md for the full manual testing guide.

  • Build: `cargo build -p tzel-wallet-app --bin wallet-server`
  • Start server with `--skip-proof` against a running `tzel-operator`
  • `GET /balance` returns `{"private_balance": 0}` on a fresh wallet
  • `POST /address` returns a valid payment address JSON
  • `POST /shield` with a funded sender succeeds and increments balance after scan
  • `POST /scan` detects incoming notes and removes spent ones
  • `POST /transfer` moves funds between two wallet-server instances
  • `POST /unshield` withdraws to a public L1 address
  • Web dapp: `cd web && npm install && npm run dev`, verify all operations via UI

🤖 Generated with Claude Code

saroupille and others added 5 commits April 17, 2026 23:51
- ocaml_unit_tests.sh: dune is installed via opam but the shell step
  did not have the opam environment activated; prefix with opam exec --
- cross_impl_interop: the test calls dune at runtime but was in the
  Rust-only job which has no OCaml; move it to the ocaml job (which
  already has OCaml) and add a Rust toolchain step there
- Remove cross_impl_interop from the rust-and-cairo job

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ML-KEM native C library (vendor/mlkem-native) is not committed to
git and must be compiled before dune can link the OCaml executables.
Add a `make lib` step and set LIBRARY_PATH for the cross_impl_interop
cargo test which runs dune at runtime outside the shell script.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes shield/transfer/unshield/scan/balance/address over a local REST
API so any HTTP client (including the bundled Vite/React dapp) can
interact with a private TzEL wallet without touching the CLI directly.

- wallet-server binary: Axum HTTP server, supports --trust-me-bro
  (skip proofs) and --proving-service=<url> for STARK proof generation
- web/: Vite + React dapp connecting to wallet-server in real mode,
  with mock mode for UI development without a live node
- core: add bech32 encoding for PaymentAddress; PartialEq/Eq on
  PaymentAddress; remove unused verify_wots_signature_against_leaf
- docs/wallet_server.md: manual testing guide

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename --trust-me-bro to --skip-proof
- Fix transfer/unshield atomicity: drop pre-POST save_wallet; notes are
  only committed to disk via finalize_successful_spend after a confirmed
  ledger response, matching the shield handler's pattern
- Normalise ledger error status codes to 502 BAD_GATEWAY across all handlers
- Change POST /address (was GET): endpoint mutates addr_counter so GET
  semantics were wrong; add block_in_place for consistency
- Remove three paraphrasing inline comments
- Update docs and web dapp to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed by mistake in the audit cleanup — the rollup kernel uses it
to authenticate signed verifier and bridge configs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant