Files
wallet/LLM.md
T
Hanzo AIandzeekay 111b7f6276 wallet/pq: concrete ML-DSA / ML-KEM / SLH-DSA + hybrid + HD + precompile encoders
Wires the canonical post-quantum stack per LP-4200 into the wallet:

  • mldsa.ts    — ML-DSA-44/65/87 providers (FIPS 204) over @noble/post-quantum
  • mlkem.ts    — ML-KEM-512/768/1024 KEM providers (FIPS 203)
  • slhdsa.ts   — SLH-DSA-128f/192f/192s/256f providers (FIPS 205)
  • hybrid.ts   — secp256k1∥ML-DSA + Ed25519∥ML-DSA containers; wire format
                  byte-compatible with luxfi/sdk wallet/keychain PQSigner
  • hdPq.ts     — BIP-32 → cSHAKE expandChildSeed → ML-DSA/SLH-DSA keypair
  • domain.ts   — FIPS-204 §5.4 ctx strings:
                    lux-evm-precompile-mldsa-v1   (0x012202)
                    lux-x-chain-utxo-v1
                    lux-p-chain-platform-mldsa-v1
                    lux-recovery-slhdsa-v1        (0x012203)
                    lux-handshake-mlkem-v1
  • precompile.ts — eth_call encoders for 0x012201/02/03 LP-4200 verifiers

The pkgs/wallet/pq surface decomplects four concerns that were previously
tangled in luxfi/sdk's PQSigner: scheme dispatch (providerFor), domain
separation (contextBytesFor), hybrid composition (encode/decodeHybrid),
HD derivation (derivePQIdentity). Each module is independently complete;
the index re-exports them as a single PQ public API.

Web facade lives at apps/web/src/lib/pq.ts; usePQIdentity() hook derives
the canonical ML-DSA-65 identity at m/44'/9000'/<chainID>'/0'/0'/0' from
the unlocked auth-slice mnemonic.

Tests: 71 passing across 8 suites
  domain: 4   mldsa: 11   mlkem: 7   slhdsa: 6
  hybrid: 9   hdPq: 9     precompile: 8   pqAccount: 17 (pre-existing)

Test runner: jest.pq.config.js (standalone — bypasses upstream-shaped
jest-expo preset still pending refactor; see LLM.md).

Domain separators pinned by domain.test.ts; cSHAKE customizations pinned
by pqAccount.test.ts (pre-existing). Replay-across-chains explicitly
tested via mldsa.test.ts and hybrid.test.ts cross-ctx verify cases.
2026-06-10 22:35:41 -07:00

9.0 KiB

@luxfi/wallet — AI Assistant Knowledge Base

Project: luxfi/wallet — canonical Lux Wallet upstream (web + mobile + extension). Org: Lux Industries Inc. (luxfi). Status: apps/web builds clean. apps/{extension,mobile} retain upstream-shaped src that requires an app-level refactor to compile against current @l.x/* npm packages. The canonical bones (pkgs/{wallet,brand,analytics}) are stable.

Canonical structure

luxfi/wallet/
├── apps/
│   ├── web/             — Vite 8 SPA, React 19, brand-aware. Builds in <100ms.
│   ├── extension/       — Chrome/Firefox MV3 (upstream-shaped; app-refactor pending).
│   └── mobile/          — React Native + Expo (upstream-shaped; app-refactor pending).
├── pkgs/
│   ├── wallet/          — `@luxfi/wallet` — canonical wallet feature bones.
│   ├── brand/           — `@luxfi/wallet-brand` — runtime white-label config.
│   └── analytics/       — `@luxfi/wallet-analytics` — provider-pluggable analytics.
├── SCREENS.md           — UX spec freeze (2025-12-15).
└── LEGACY.md            — Sibling repos (wallet-legacy, wwallet, xwallet) to archive.

Build commands (verified working)

pnpm install --no-frozen-lockfile
pnpm --dir apps/web build      # Vite SPA — clean. Output in apps/web/dist/.

apps/{extension,mobile} build via Nx targets that depend on workspace packages still upstream-shaped (@universe/*, wallet/* direct paths) and will not type-check or build until refactored to consume the npm-published @l.x/* and the workspace @luxfi/wallet. Track in this file when fixed.

Foundation slice (apps/web shell)

apps/web is the Vite SPA shell. Provider tree:

GuiProvider → QueryClientProvider → WagmiProvider → RouterProvider
  • src/main.tsx — awaits loadBrandConfig() before first render.
  • src/App.tsx — wraps the four providers above.
  • src/router.tsx — react-router-dom v7 with lazy-loaded screen modules.
  • src/components/AppShell.tsx — top bar + side drawer + Outlet.
  • src/components/ChainSwitcher.tsx — native <select> over brand.supportedChainIds.
  • src/components/GuiProvider.tsx — passthrough today; swap to HanzoguiProvider once @hanzo/gui@7 republishes its missing dist/ artifact.
  • src/config/wagmi.tsbuildWagmiConfig() reads brand.supportedChainIds and resolves transports via getBootnodeRpcUrl(chainId). Chains without a resolvable RPC are dropped — never http("").
  • src/config/queryClient.ts — 30 s stale, retries off, no focus refetch.
  • src/store/index.ts — zustand account / chain / ui slices owned by Foundation. Other slices (auth, send, swap, stake) are sibling files owned by other Blues.
  • src/hooks/useAccount.ts — wagmi + zustand union; the only useAccount screens should import.
  • src/hooks/useBrand.ts() => brand for React-friendly typing.

Screen Blues fill src/screens/{name}/index.tsx; today each is a labelled placeholder so the build is clean and merges are pure file replacements.

White-label brand pattern (canonical)

Brand config flows at runtime, not build time. Same pattern as ~/work/lux/exchange/pkgs/config/src/brand.ts.

@luxfi/wallet-brand/brand.json   ─copy→  apps/web/dist/brand.json
                                                │
                                                ↓
                                  K8s ConfigMap mount overlays
                                                │
                                                ↓
                                  loadBrandConfig() fetches /brand.json
                                                │
                                                ↓
                                  mutates `brand` singleton + CSS vars
                                                │
                                                ↓
                                  React renders against final brand

White-labels (Liquidity, Zoo, Pars) override /brand.json via a K8s ConfigMap — no source fork required. Theme tokens are CSS custom properties (--accent1, --surface1, etc.) so the same DOM tree renders any brand.

Analytics pattern (canonical)

@luxfi/wallet-analytics provides a provider-pluggable AnalyticsDriver interface. No third-party SDK loaded by default. Hanzo Insights is the intended default driver (registered at app bootstrap). White-labels swap drivers at boot via setAnalyticsDriver(...). Surface matches what wallet call sites expect (init/track/identify/Identify/...).

RPC pattern (canonical)

getBootnodeRpcUrl(chainId) from @luxfi/wallet-brand is the only way to resolve an RPC endpoint. Default is https://<gatewayDomain>/v1/rpc/<chainId> with runtimeConfig.rpc[<chainId>] overrides honored first. No Quicknode, no Alchemy direct. White-labels point at their own gateway (e.g., gw.lux.exchange/v1/rpc/96369).

Direct dependency hygiene

  • Zero @datadog/* direct deps in any app package.json or pkgs/*. (Transitive deps via @l.x/utils and @l.x/lx remain — those are upstream packages and will be cleaned in their own pkg releases.)
  • Zero @amplitude/* direct deps.
  • Zero @luxamm/* direct deps (forked to @luxamm/* per commit 81b2ba3e).
  • Zero getQuicknodeEndpointUrl references in our source — only the string literal 'quicknode' exists in error-matching test fixtures.

Workspace catalog (pnpm-workspace.yaml)

@hanzogui/*-fork.1 aliases for React Native packages. The @hanzogui prefix is the internal umbrella name. Product brand for the GUI library is @hanzo/gui.

Known transitive type errors (NOT our bug)

@l.x/api, @l.x/lx, @l.x/utils, @l.x/config ship as raw .ts files and reference a rootless utilities/src/* module that doesn't exist on disk. This means tsc --noEmit against pkgs/wallet/ traces into node_modules/.pnpm/@l.x+* and produces ~5000 errors. These are upstream-publishing bugs in the @l.x/* packages — they need to ship .d.ts artifacts and self-resolved imports. Filed as a follow-up; lux/wallet does not patch upstream packages.

The web SPA builds because Vite tree-shakes — only used surface is touched.

Sibling repos (see LEGACY.md)

  • ~/work/lux/wallet-legacy — OneKey-fork lineage. BRAND_PACKAGE pattern absorbed → archive.
  • ~/work/lux/wwallet — bespoke SDK line. Superseded by @l.x/api → archive.
  • ~/work/lux/xwallet — OKX-fork lineage. Hardware support already removed → archive.
  • ~/work/lux/dwallet — Desktop product. Independent, do not fold in.

Rules for AI Assistants

  1. NEVER write random summary files — update LLM.md only.
  2. NEVER commit symlinked files (.AGENTS.md, CLAUDE.md, etc.) — they're in .gitignore.
  3. NEVER introduce direct @datadog/*/@amplitude/*/@luxamm/* deps — use the abstractions in pkgs/analytics and @luxamm/*.
  4. NEVER reference getQuicknodeEndpointUrl — use getBootnodeRpcUrl.
  5. NEVER hardcode brand strings ("Lux Wallet", "lux.network") in app code — read from the brand singleton in @luxfi/wallet-brand.
  6. NEVER write the banned forked-UI brand — use "@hanzo/gui" / @hanzogui/* umbrella.
  7. ALWAYS preserve BIP44 path 9000 for Lux P/X chain addresses in any key derivation (60 for EVM C-chain).
  8. ALWAYS keep the GPL-3.0-or-later license header — wallet inherits it.
  9. ALWAYS route PQ crypto through pkgs/wallet/src/features/wallet/pq/ — ML-DSA / ML-KEM / SLH-DSA / hybrid / HD-PQ derivation / domain separators / precompile encoders all live there. Re-export from apps/web/src/lib/pq.ts.

PQ stack (LP-4200, FIPS 203/204/205)

Canonical entry: pkgs/wallet/src/features/wallet/pq/index.ts. Web facade: apps/web/src/lib/pq.ts. React hook: apps/web/src/hooks/usePQIdentity.ts.

Module Purpose
pqAccount.ts shape, cSHAKE AccountID, MLDSAProvider iface, HD-path strings
domain.ts FIPS-204 ctx strings (evm-precompile, x-chain-utxo, …)
mldsa.ts ML-DSA-44/65/87 providers via @noble/post-quantum
mlkem.ts ML-KEM-512/768/1024 KEM providers
slhdsa.ts SLH-DSA-128f/192f/192s/256f providers
hybrid.ts Ed25519∥ML-DSA + secp256k1∥ML-DSA containers (luxfi/sdk-compat)
hdPq.ts BIP32 → expandChildSeed → ML-DSA/SLH-DSA keypair
precompile.ts eth_call wrappers for 0x012201/02/03

Tests run via pnpm --dir pkgs/wallet exec jest --config jest.pq.config.js (standalone — bypasses the upstream-shaped jest-expo preset that's still pending refactor). 71 tests passing as of 2026-05-18.


Single source of truth for AI assistants on luxfi/wallet. Update this file when behavior changes; never spawn parallel .md documents.