Files
zeekay 2e1b7dcaf8 feat: @luxwallet/create — stamp a branded Lux Wallet from one brand.json
Generator that scaffolds a ready-to-run branded Lux Wallet web app for a new
chain/org from a single BrandConfig. One command per new chain.

- CLI `create-lux-wallet <brand-ref> [outDir] [--force]` + programmatic
  generate(brand, outDir): validates the brand via @luxwallet/brand's
  defineBrand, then stamps apps/web/ (Vite + React) that mounts @luxwallet/ui
  via WalletUIProvider, authenticates with @hanzo/iam (HIP-0111), reaches
  chains through @luxwallet/sdk, and ships the brand's /brand.json.
- DRY runtime-brand.json strategy: ONE brand-neutral app shell, byte-identical
  across brands (enforced by test). Only public/brand.json + public/logo.svg
  differ. No per-brand forks. *.tmpl files get {{brand.*}} interpolation.
- Example brands lux/hanzo/zoo/pars with canonical @luxwallet/chains ids
  (lux-c 96369, zoo 200200, hanzo 36963, pars 494949) + _template.json.
- New chains: add to @luxwallet/chains registry, reference id in brand.chains[].
- MIT, zero GPL — never imports @luxfi/wallet. @luxwallet/* + @hanzo/* are
  workspace deps with a documented switch to npm versions once published.

Verified headlessly: CLI tsc --noEmit clean; 7/7 node:test pass (tree,
brand.json validity via defineBrand, no token leak, DRY shell identity, force,
resolution); sample generation of all four brands validates.
2026-06-23 15:28:28 -07:00
..

# {{brand.name}}

A branded [Lux Wallet](https://github.com/luxwallet) web app for **{{brand.name}}**
(`{{brand.id}}`), generated by [`@luxwallet/create`](https://github.com/luxwallet/create).

This is a thin Vite + React shell that mounts `@luxwallet/ui` screens, authenticates
with Hanzo IAM via `@hanzo/iam` (HIP-0111), and reaches chains through `@luxwallet/sdk`.
**The shell is brand-neutral** — everything brand-specific lives in
[`public/brand.json`](./public/brand.json), loaded at runtime by
`@luxwallet/brand`'s `loadBrandConfig()`. To re-brand, edit that one file (and
`public/logo.svg`); never fork the app.

## Prerequisites

> The `@luxwallet/*` and `@hanzo/*` packages are **not yet published to npm**.
> Until they are, install them as workspace/path deps:
>
> - In a pnpm monorepo: set each to `"workspace:*"` in `package.json`.
> - Standalone: use `"file:../path/to/sdk/packages/<pkg>"` etc.
>
> The versions pinned in `package.json` are the (future) npm versions to switch
> to once published. See the `//deps` note in `package.json`.

## Run

```bash
pnpm install     # or npm install
pnpm dev         # Vite dev server on http://localhost:5174
```

## Build (static export)

```bash
pnpm build       # → dist/
pnpm preview     # serve the production build locally
```

`dist/` is a static SPA. Deploy it to any static host (GitHub Pages, S3 +
`hanzoai/static`, etc.). The host must serve `index.html` for unknown routes so
the IAM callback path (`/auth/callback`) and client-side routing work.

## IAM registration (one-time, per brand)

Register an IAM application (HIP-0111) with:

- **client_id**: `{{brand.iam.clientId}}` (`<org>-<app>`)
- **redirect URI**: `https://<your-deploy-host>/auth/callback` (exact match)
- **scopes**: `openid profile email`

The superuser convention is `z@{{brand.domain}}` / `Ilove<App>2026!!`.
Secrets live in KMS — never in this repo.

## Brand config

[`public/brand.json`](./public/brand.json) is the validated brand. Its shape is
the `BrandConfig` from `@luxwallet/brand`:

| field            | meaning                                                      |
|------------------|-------------------------------------------------------------|
| `id`             | stable brand id (`{{brand.id}}`)                            |
| `name`           | display name (`{{brand.name}}`)                             |
| `shortName`      | compact label, e.g. "Continue with `{{brand.shortName}}`"   |
| `domain`         | primary domain (`{{brand.domain}}`)                        |
| `iam`            | OIDC `serverUrl` + `clientId` + `scopes`                    |
| `gateway`        | `rpcBaseUrl`; chains reached at `<rpcBaseUrl>/v1/rpc/<id>`  |
| `theme`          | token → color, applied as `--lw-<token>` CSS vars          |
| `chains`         | chain ids from `@luxwallet/chains` to enable               |
| `logo`           | optional logo URL (defaults to `/logo.svg`)                |

## Adding a chain

Chains are defined ONCE in `@luxwallet/chains` (the registry), not here. To add a
new sovereign-L1 chain to this wallet:

1. Add the chain's entry to the `@luxwallet/chains` registry (id, evmChainId,
   gateway `rpcRoute`, BIP-44, native asset).
2. Reference its **id** in `public/brand.json`'s `chains[]`.

Then `@luxwallet/sdk` resolves its RPC via the brand gateway automatically.

## Web3 wallet (SIWx) login — optional

The chain picker in `src/Login.tsx` drives the multi-chain SIWx flow handled by
`@luxwallet/connect` (EVM / Solana / Bitcoin / TON / XRP). That package is an
optional peer dep and is **not installed by default**. To enable it, add
`@luxwallet/connect` and follow the TODO in `src/Login.tsx`.