90 Commits
Author SHA1 Message Date
zeekay b0e0d789f9 chore: sync working tree
Commits 4 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 10:06:48 -07:00
zeekayandHanzo Dev 75c529deb8 fix(wallet-web): visible brand marks, centered auth card, spaced CTAs, no cross-brand chain leak
- brands/{lux,zoo,hanzo}.svg: real marks (violet Lux prism, colorful Zoo venn,
  Hanzo block-H). The prior rect fill="currentColor" + black glyph collapsed to a
  solid black square in the light-theme header (currentColor is the dark text color).
- gui-stub tokenize(): numeric Tamagui space/size tokens ($3/$6/$10) now resolve to
  px on a 4px scale. They were mapped to undefined CSS vars (var(--3)), so every
  gap/padding/fontSize was silently dropped — the source of the gap-less "button
  blob" (colliding radii) and the top-anchored auth card.
- Welcome: plain inline-styled, viewport-centered card with a max-width panel, the
  brand mark, and 44px CTAs separated by real gaps (matches AppShell/Callback pattern,
  independent of the unresolved Tamagui token contract).
- overlays/{lux,zoo,hanzo}/brand.json: drop sibling first-party L1s from the chain
  selector. Zoo no longer lists "Lux C-Chain"/"Hanzo"; each brand keeps its own
  networks + neutral public EVMs (Ethereum/Arbitrum/Base/Polygon/Avalanche).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-19 01:30:36 -07:00
zeekayandHanzo Dev 6dc2c4e9b4 deploy(wallet-web): pin lux overlay to 21986289d (font + overflow fixes)
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 23:47:10 -07:00
zeekayandHanzo Dev 21986289db fix(web): global sans-serif font — wallet was rendering in UA serif (Times)
The SPA ships no font files and never set a font-family, so every element fell
back to the browser's serif default (Times) — the landing page read as
"unstyled" even though the brand theme (accent/surface/neutral CSS vars) was
being injected correctly by loadBrandConfig. Set a system sans-serif stack on
html/body (zero bundled assets, crisp on every platform) and force form controls
(button/select/input) to inherit it, since they don't by default.

Verified headless (Chrome channel, 390px): body font-family was "Times" before;
this makes it the system sans stack. Pairs with the prior overflow fix.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 23:46:52 -07:00
zeekayandHanzo Dev 5dd2b16022 fix(web): stop mobile horizontal overflow on the wallet landing page
At 390px the top bar was a single non-wrapping flex row whose min-content
width (~470px) forced the single-column app grid — and thus the whole
document — to 478px, pushing the account/connect control off-screen and
clipping the CTA card. Two changes:

- index.html: add the app's one document-level reset (box-sizing:border-box
  + zero html/body margin). The missing UA body margin reset was adding the
  extra 8px (478 vs 470). No overflow-x:hidden — it would break the sticky
  header.
- AppShell.tsx: let the header wrap (flex-wrap) so the chain+account group
  drops to a second row instead of widening the document; grid header row
  becomes minmax(56px,auto) so the wrapped row isn't clipped; groups get
  min-width:0 so they can shrink.

Verified headless at 390x844: document.scrollWidth 478 -> 390, zero
elements past the viewport, connect control + CTA fully on-screen.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 20:48:55 -07:00
zeekayandHanzo Dev db275f7487 deploy(wallet-web): lux overlay tag-pin fix + plain-manifest fallback
Pin the image tag the way a custom CR requires: the lux.cloud/v1 Service CR
holds the tag in structured spec.image.tag, which the kustomize images:
transformer does NOT reach — so it is pinned via a JSON patch (was silently
staying at :latest). Set to the built sha dc08a6af0.

Add fallback.yaml: equivalent plain manifests (Deployment + Service + Ingress +
cert-manager Certificate via ingress-shim / letsencrypt-prod DNS-01). lux-k8s
runs lux-operator scaled to 0, so services.lux.cloud does not reconcile; these
reproduce exactly what it would generate, letting wallet.lux.network go live
without scaling the operator (which would touch other lux-mainnet CRs/luxd).
Follows the proven safe.lux.network ingress pattern (class ingress, DNS-01).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 14:37:11 -07:00
zeekayandHanzo Dev dc08a6af0d fix(web): copy root tsconfig.base.json into Docker build stage
The apps/web Dockerfile selectively COPYs workspace sources but omitted the
root tsconfig.base.json that pkgs/{wallet,brand,analytics,chains} each `extends`.
Vite 8's rolldown/oxc TS transform resolves the extends chain at build time and
hard-fails ("Tsconfig not found /app/tsconfig.base.json", surfaced only as the
opaque `errors: [Getter/Setter]`) when the base is absent. Local vite build
masks it (runs against the full repo tree where the base exists); every
containerized build failed regardless of OS/arch. Reproduced in node:22-alpine
and verified fixed — dist/ now produced.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 14:21:38 -07:00
zeekayandHanzo Dev 5754513482 fix(web): bridge onChangeText->onChange in gui-stub Input so onboarding accepts input
The auth screens (ImportMnemonic/SetPIN/Unlock/ConfirmMnemonic) speak the
Tamagui/RN text-input contract (onChangeText, secureTextEntry, multiline,
onSubmitEditing) - the same contract @luxfi/ui@7.4.0's bridged Input honors.
The local @hanzo/gui stub spread these raw onto a native <input>, so a
value + onChangeText field was controlled with no working handler and
silently dropped every keystroke (dead seed-phrase / PIN entry). Translate
the contract to native web events in the stub.

Verified 390x844: import-phrase textarea + PIN + unlock accept keystrokes,
create/import persists, unlock reaches /portfolio, quick-actions soft-nav
without session drop.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 10:50:45 -07:00
zeekayandHanzo Dev 1ddc0df258 fix(web): dashboard quick-actions use SPA soft-nav so the session survives
The wallet keeps `isUnlocked` + the plaintext mnemonic in memory ONLY (never
persisted — see the auth store's partialize), so ANY full-document reload wipes
the session and bounces the user back to onboarding. On mobile this made the
wallet unusable: every full-reload dashboard action effectively logged you out.

- Portfolio dashboard: render the Send / Cross-Chain / Earn / Manage Keys
  quick-action cards as react-router <Link> (client-side soft-nav) instead of
  full-reload <a href>. A tap pushState-navigates and the in-memory unlock +
  mnemonic survive.
- Audit for other session-dropping full-reload links: Callback's "Back to
  wallet" recovery link was a raw <a href="/"> — converted to <Link to="/">.

Verified at 390x844 (iPhone-class): the 4 cards render with no horizontal
overflow (documentElement.scrollWidth == 390); tapping Send soft-navigates to
/send with NO document reload (a page sentinel + the zustand auth store both
survive, isUnlocked stays true); a full-reload navigation (the prior behavior)
wipes the in-memory session — confirming the fix.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 10:43:17 -07:00
zeekay 83e2706fb6 remove Stripe from onramp deep-link test fixtures (Stripe account disabled)
Stripe is no longer an available fiat on-ramp provider. Drop it from the
buy-link provider test fixtures; moonpay+coinbasepay still exercise the
multi-provider parse and case-normalization paths.
2026-06-24 12:34:20 -07:00
zeekay a86efad105 chore(deps): lockfile → @luxwallet/* from npm (no file:) 2026-06-24 11:16:40 -07:00
zeekay 48915e47db chore(deps): @luxwallet/chains + @hanzo/iam file: → published npm 2026-06-24 11:11:51 -07:00
zeekay 9d6c1d9a0c feat(web): chain registry from @luxwallet/chains + IAM login via @hanzo/iam
Chain metadata is now sourced from the canonical @luxwallet/chains registry
(MIT) instead of three duplicated hardcoded maps:
  - new lib/chains.ts adapter: chainLabel / evmChainDef / evmChainIds /
    registryChains, with a UI-only block-explorer overlay.
  - config/wagmi.ts, components/ChainSwitcher.tsx, lib/brand.ts all read it.
  - the wallet now recognises/holds every Lux Bridge chain: Lux C/X/P/Q/Z,
    sovereign L1s (Hanzo/Zoo/Pars/Sparkle Pony), external EVM (Ethereum,
    Arbitrum, Base, Polygon, Optimism, Avalanche) and non-EVM bridge families
    (Bitcoin, Solana, TON, XRP, Polkadot). brand.supportedChainIds still
    narrows the active wagmi/switcher surface per white-label.

IAM account login is delegated to the canonical @hanzo/iam SDK (MIT, HIP-0111
one-way auth) — lib/iam.ts is now a thin brand-aware facade over
@hanzo/iam/browser, keeping its existing surface (beginLogin/completeLogin/
getAccessToken/hasSession/clearSession) so store/session, lib/custody and the
callback route are unchanged. Adds a 'Sign in with <brand>' button to the
Welcome screen (additive, alongside local create/import).

The wallet's own keyring + signing path is untouched: this is the chain
metadata source + IAM account login, not external-wallet-connect.

Deps (file: for now, TODO npm): @luxwallet/chains, @hanzo/iam — both MIT,
zero new GPL.
2026-06-24 09:15:08 -07:00
zeekay 82e0d5512e fix(web): re-sync store chainId to brand.defaultChainId after loadBrandConfig (white-label default chain) 2026-06-23 16:27:05 -07:00
zeekay 3b1ad33433 test(backend): LIVE backend↔MPC custody e2e (not mock)
The not-mock counterpart to internal/api/e2e_test.go (which wires the real
custody adapter to a MOCK lux/mpc). This drives the wallet backend's /v1 API
against a REAL 3-node mpcd ensemble (consensus mode, the production code path)
over the live HTTP /keygen + /sign endpoints.

Path (a) full-OIDC: a local RSA-signed OIDC issuer + the real iam.Verifier
(standard discovery + JWKS + RS256 verify) + real org-scoping (owner claim) +
the real api.Server /v1 handlers + custody.NewMPC pointed at live node0. Proves
the full chain: lux.id verify → org scope → LIVE MPC threshold keygen + sign,
with no plaintext key in any backend response or MPC node log.

Exercises POST /v1/wallets (live keygen → real evm address), GET /v1/wallets,
POST /v1/wallets/{id}/sign (live threshold signature), backend-level idempotency
(same key → same signature), and the missing-idempotencyKey → 400 boundary.

//go:build integration. Builds mpcd from the lux/mpc repo (LUX_MPC_REPO, default
/Users/z/work/lux/mpc); skips cleanly if that repo is absent. Reuses the OIDC
helpers (newIDP/tokenForOrg/do) from api_test.go; stdlib-only ids (no new dep).

Run: go test -tags integration ./internal/api/ -run TestBackendLiveMPC -v
2026-06-23 16:24:27 -07:00
zeekay 611a3d77da docs(LLM): extend App(Wallet) spec — native binaries + download deployables, per-brand chainDefault, live MPC /sign 2026-06-23 16:00:04 -07:00
zeekay 10c926c22a feat(web): white-label custody wiring + per-brand deploy + download page 2026-06-23 15:51:02 -07:00
zeekay 64bd0f67c9 feat(backend): App(Wallet) MPC custody server (Go, no plaintext keys)
Add apps/backend — the wallet's custody server. The load-bearing Front-E gap
(repo was FE-only) is now closed: a wallet account model backed by MPC custody
where the private key is split t-of-n across the MPC nodes (lux →
mpc.lux.network) and NEVER assembled — this service stores only public keys +
addresses.

- internal/iam: lux.id OIDC bearer verification via JWKS (discovery + RS256/
  ES256, alg-confusion-safe — HMAC/none refused). Org from the 'owner' claim.
  golang-jwt/v5 (matches the lux ecosystem), stdlib JWKS fetcher (no extra dep).
- internal/custody: the Custodian port (a value the wallet owns) + HTTP adapter
  to lux/mpc's verified /keygen + /sign wire. Mirrors mpc client.Threshold's
  security contract (idempotency required, org-scoped). PQ-ready: mldsa65 is a
  first-class scheme (the PQ primitives stay in pkgs/wallet/.../pq + consensus —
  not duplicated). No in-process key fallback — unconfigured MPC is an error.
- internal/store: wallet↔org↔addresses (no key field). Org-scoped reads — a
  cross-org get is ErrNotFound (no existence disclosure).
- internal/api: /v1/* (no /api/ prefix). IAM-gated, org-scoped. POST/GET
  /v1/wallets, GET /v1/wallets/{id}, POST /v1/wallets/{id}/sign, GET /v1/health.
- cmd/wallet-backend: env config (white-label per tenant: IAM_ISSUER/AUDIENCE,
  MPC_ENDPOINT — shared hanzo-mpc or BYOMPC), JSON logs, graceful shutdown.

Deploy (native CI + operator, NO GHA): build-only .platform.yml → ghcr.io/
luxfi/wallet-backend on lux-build pools; lux operator rolls
apps/backend/k8s/wallet-backend.yaml (lux.cloud/v1 Service + KMSSecret for
MPC_SERVICE_TOKEN), ingress wallet-api.lux.network.

17 tests green (go test ./...): IAM (valid/no-org/wrong-aud/expired/alg-
confusion), custody (no-key-leak, idempotency-required, PQ-accepted, no-
fallback), API (auth-gate, org-isolation cross-org→404, create→sign), + a full
E2E through the real adapter→mock-MPC→real-server. Binary boots, /v1/health ok,
/v1/wallets 401 unauth. LLM.md captures the App(Wallet) spec for the unified
operator's App Kind.
2026-06-23 15:07:40 -07:00
Antje WorringandHanzo Dev e291c38b74 docs: tidy LLM.md indexes; CLAUDE.md -> LLM.md symlink convention
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-17 09:56:30 -07:00
zeekay fc08afc683 corona→corona: academic Corona now only in lp-220-p3q-corona 2026-06-11 10:28:44 -07:00
zeekay 0cc382741c scrub Liquidity branding + corona→corona (OSS brand hygiene) 2026-06-11 00:01:24 -07:00
Hanzo Devandzeekay aaae4e0278 scrub Avalanche naming residue: comment rename in non-EVM chain balance helper 2026-06-10 22:35:41 -07:00
e00da8f14f Add agent project docs (CLAUDE.md, LLM.md)
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-10 22:35:41 -07:00
Hanzo Devandzeekay 5d7a45aaec scrub: subnet/l2 → chain (canonical vocabulary, forward-only)
Wire-format codec IDs unchanged. CLI aliases deleted; chain is the
command. No backwards-compat shims, no deprecation comments.
2026-06-10 22:35:41 -07:00
Hanzo Devandzeekay 5daf0e5517 brand: rip <tenant> reference from chain comments (no crossover) 2026-06-10 22:35:41 -07:00
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
Hanzo Devandzeekay efdbc6a717 docs: add LICENSING.md pointing at canonical Lux IP strategy
Single LICENSING.md file referencing the canonical three-tier IP and
licensing strategy at github.com/luxfi/.github/blob/main/profile/README.md.

LICENSE file is unchanged; this only adds a navigational pointer.
2026-06-10 22:35:41 -07:00
Hanzo AIandzeekay e85de16ef5 chore(wallet): bump @luxfi/wallet to 1.0.11
Ships PQAccount TS mirror (cSHAKE-256 AccountID derivation +
HD-path formatters) added in #3.
2026-06-10 22:35:41 -07:00
Hanzo AIandzeekay 8cd7edb960 fix(wallet/pq): satisfy --strict exhaustive-switch narrowing
The switch in walletSchemeName covers every WalletSchemeID enum
value, so TypeScript --strict narrows the default arm's `s` to
`never` and rejects `s.toString(16)`. Cast through `number` for
the forward-compat fallback path.

No behaviour change; KAT vector for AccountID derivation
(chainID=9000, scheme=0x42) still matches the Go side at
76a03630148103ec558cf4d8f7e8a2d8766ea205d96a4529249c3aaf9c5d078cc9f9fdb8f21a5e7ef6bb4c4ea29c9654.
2026-06-10 22:35:41 -07:00
Hanzo AIandzeekay d3807df45d wallet/pq: PQAccount TS mirror of luxfi/sdk wallet/account
TypeScript module under pkgs/wallet/src/features/wallet/pq/ that
mirrors the PQAccount shape, the cSHAKE-256 AccountID derivation, the
HD-path formatters, and the role/customization mappings defined on the
Go side in github.com/luxfi/sdk/wallet/account.

The TS side intentionally does NOT pull in an ML-DSA-65 implementation.
ML-DSA itself is software-in-browser; the cost of bundling FIPS 204 is
not worth paying for read-only consumers (UI list views, route
handlers). Consumers that need to sign in-browser supply a concrete
MLDSAProvider implementation (typically backed by @noble/post-quantum
mlDsa65 or a hardware-wallet bridge). Browser keys are session-scoped;
long-lived keys MUST live in a native wallet.

Wire compatibility: AccountID KAT cross-checked against the Go side.
For the canonical vector (chainID=9000, scheme=0x42, pubkey="test-
pubkey-for-accountid-kat-vector-stable-across-runs") both
implementations produce
76a03630148103ec558cf4d8f7e8a2d8766ea205d96a4529249c3aaf9c5d078cc9f9fdb8f21a5e7ef6bb4c4ea29c9654.

Adds @noble/hashes ^1.7.1 to pkgs/wallet dependencies (already
transitively present via @scure/bip32; declaration is for strict
node_modules resolution). pnpm-lock.yaml refresh on next install.

Patch-bump.
2026-06-10 22:35:41 -07:00
Hanzo AIandzeekay 9e53916781 chore: scrub uniswap — use @luxamm/* (lux fork) only
Replace every @uniswap/* package import with @luxamm/* (lux fork
already canonical for SDK pieces). Replace app.uniswap.org URLs with
lux.exchange, uniswap.org with lux.network. Replace brand strings
(Uniswap Wallet → Lux Wallet, Uniswap Extension → Lux Extension,
Uniswap Labs → Lux Labs Inc.) where they appear in user-facing UI.
Rename handleUniswapAppDeepLink → handleLuxAppDeepLink, also rename
focusOrCreateUniswapInterfaceTab → focusOrCreateLuxInterfaceTab.

Manifest host_permissions updated to lux.exchange and *.lux.network.

Lockfile regeneration: TODO — run pnpm install to refresh after this
commit. Transitive @uniswap/* references in pnpm-lock.yaml are
upstream-package internals.

Protocol-level identifiers (UniswapMethodHandler, UniswapMethods,
UniswapOpenSidebarRequest, handleUniswapX, UniswapXOrderDetails) are
preserved because they reference wire-protocol method names
(uniswap_openSidebar) and external SDK type exports — renaming would
break upstream contracts. iOS Uniswap target directory and
uniswapteam.slack.com / github.com/Uniswap/universe doc refs are
out-of-scope per directive (separate native-build cleanup).

Same hard-banishment rule as tamagui (per feedback memory).
2026-06-10 22:35:41 -07:00
Hanzo AIandzeekay dc12b4deed fix(extension): add missing getTsconfigAliases import + publicAssetsVariant const
wxt.config.ts referenced both but neither was imported/defined.
- Import getTsconfigAliases from ./config/getTsconfigAliases
- Define publicAssetsVariant from WXT_PUBLIC_ASSETS_VARIANT env (default: prod)

Without these, wxt build fails: getTsconfigAliases is not defined.
2026-06-10 22:35:40 -07:00
Hanzo AIandzeekay 2b7990fa0c fix(extension): add missing vite-plugin-svgr + commonjs + node-polyfills + tsconfig-paths
wxt.config.ts imports these but they were never declared in package.json
(probably stripped along with tamagui cleanup or a prior cleanup pass).
Without them, `pnpm exec wxt build` fails: Cannot find module 'vite-plugin-svgr'.
2026-06-10 22:35:40 -07:00
Hanzo AIandzeekay 58e646e718 chore: scrub tamagui — use @hanzo/gui (and @hanzogui/* forks) only
Tamagui is banned per directive. Replace every import/reference with
@hanzo/gui or the @hanzogui/* equivalent. Removes:
- tamagui-loader and tamaguiPlugin from apps/extension webpack/wxt configs
- @tamagui/babel-plugin TODO block from apps/mobile/babel.config.js
- 'tamagui', '@tamagui/web' from optimizeDeps.include hints
- @tamagui/core/reset.css side-effect imports → @hanzogui/core/reset.css
- TamaguiProvider in mobile tests → GuiProvider from gui-provider
- TamaguiInput type aliases in extension Input.tsx → GuiInput
- declare module 'tamagui' in env.d.ts → declare module '@hanzo/gui' (uses GuiGroupNames)
- '.tamagui' ignore patterns in .gitignore / .fingerprintignore
- 'tamagui-loader' from .depcheckrc

Source files: 18 .ts/.tsx files updated to import from @hanzo/gui /
@hanzogui packages. Tests + config: 4 files updated to drop
tamagui-specific behavior. Docs (LLM.md, README.md, RABBY_FEATURES.md):
updated to reflect new stack.

No tamagui entries remain in any package.json.
2026-06-10 22:35:40 -07:00
Hanzo AIandzeekay 64268180bf fix(extension): repair svg-import-fix transform body + restore optimizeDeps wrapper
The transform function for the 'svg-import-fix' plugin was missing its body
and return statement. The list of bundled deps that follows ('tamagui',
'@tamagui/web', etc.) was orphaned without an optimizeDeps.include wrapper.
Restored both:
- transform body returns code with import { ReactComponent as N } from '*.svg'
- optimizeDeps.include array properly opened/closed around the dep list
2026-06-10 22:35:40 -07:00
Hanzo AIandzeekay 79a4645aac fix(extension): remove orphaned ternary fragment from wxt.config.ts
Lines 110-115 contained a leftover dynamic-manifest ternary fragment
with no condition — broke parse and prevented `pnpm exec wxt build`
from running. Drop the dead code; manifest defines stay in the inline
manifest field above and tenant overlays via manifest.overlay.json.
2026-06-10 22:35:40 -07:00
Hanzo AIandzeekay d8f73616f0 wallet: scrub Avalabs IP brand refs 2026-06-10 22:35:40 -07:00
Hanzo AI 9b619517bc feat(chains): @luxfi/wallet-chains — viem chain defs for Lux ecosystem
Canonical home for static viem `defineChain` configs across the Lux
ecosystem (lux/build, lux/wallet, third-party integrators). Ships
lux/luxTestnet/luxDex, zoo/zooTestnet, hanzo/hanzoTestnet,
sparklePony/sparklePonyTestnet, pars/parsTestnet plus collection
re-exports (LUX_CHAINS, ZOO_CHAINS, ...). Published to npm as
@luxfi/wallet-chains@0.1.1 (tsup esm + dts, viem peerDep ^2.30).

White-label deployments overlay their own chain configs at runtime
via the brand.json layer in @luxfi/wallet-brand and do not consume
this package directly.
2026-04-30 17:57:33 -07:00
Hanzo AI e068c079b8 docs(legacy): wwallet migration audit + local archive (2026-04-30)
Walked every dir under wwallet/{app,web,components,sdk}. Zero files ported.
Canonical luxfi/wallet already covers every feature wwallet had with a
stricter security model and the SDK already published on npm.

- Local: mv ~/work/lux/wwallet ~/work/lux/.wwallet-archived-2026-04-30
- GitHub: gh repo archive luxfi/wwallet pending org admin permission
2026-04-30 17:54:39 -07:00
Hanzo AI 29eab77bd2 feat(bridge): Bridge form + Lux Teleport quote/execute hooks
SCREENS.md §5. Wires /bridge to the cross-chain transfer form: source/
dest chain selectors, asset amount, recipient (same wallet | other),
route preview (lock → MPC → mint), threshold notation.

  Bridge.tsx           : form, ChainPair, AssetAmount, Recipient,
                         RoutePreview; status-driven button label
                         (Locking → MPC ceremony → Minting → Bridged)
  useBridgeQuote.ts    : gateway /v1/bridge/quote; 300ms debounce, 5s
                         timeout, AbortController on input change
  useBridgeExecute.ts  : 3-step Teleport flow:
                         1. approve (ERC-20) + lock (lockToken/lockNative)
                         2. poll M-Chain MPC ceremony (2s interval, 5m cap)
                         3. surface destination mint tx hash
                         User signs ONLY leg 1; mint is signed by the
                         bridge committee via threshold MPC. This is what
                         makes Teleport feel like a transfer, not two txs.

Threat model:
  - destChainId + recipient pinned in lock calldata before the committee
    sees the message. A compromised gateway cannot redirect funds.
  - Ceremony URL built from brand.gatewayDomain (immutable per build,
    ConfigMap-overridden per environment).
  - Quote staleness >30s rejected; users can't sign locks against
    quotes that have aged out.
  - lockContract zero-address guard prevents accidental burns when the
    Teleport factory hasn't been deployed on the source chain yet.
  - Non-EVM source chains explicitly refused (clear error, not silent
    failure) — Lux P/X bridges go through @l.x/api in a later slice.
2026-04-30 17:54:01 -07:00
Hanzo AI 8bb1343757 feat(swap): SwapForm + TokenSelector + quote/execute hooks
SCREENS.md §3. Wires /swap and /swap/confirm to the canonical token-swap
form: from/to TokenSelector × 2, decimal amount, slippage popover (10/50/
100bps presets + custom), QuoteRoute panel with route kind badge.

  Swap.tsx           : main form, flip button, slippage popover, submit gate
  TokenSelector.tsx  : modal w/ search + popular pinned + inline balances
  QuoteRoute.tsx     : route kind ("AMM v2/v3" | "DEX v4") + path + impact
  useSwapQuote.ts    : gateway /v1/quote (preferred) + on-chain QuoterV2
                       fallback via useReadContract; 300ms debounce, 5s
                       timeout, AbortController on input change
  useSwapExecute.ts  : approve (max-uint, ERC-20 only) + swap via wagmi
                       writeContract; amountOutMin = amountOut * (1 - bps)
  contracts.ts       : minimal QuoterV2 / SwapRouter02 / Teleport ABIs +
                       per-chain address table (gateway is source of truth
                       at runtime; addresses are fallback-only)

Threat model:
  - Slippage enforced on-chain (router reverts on amountOutMin); UI gate is
    defense-in-depth, not the canonical control.
  - Quote staleness rejected client-side (>30s) to prevent stale-price
    execution after the user steps away.
  - All RPC URLs come from getBootnodeRpcUrl(chainId); no empty-string URLs
    (Solana Connection ctor crash mitigation per v0.3.39 lesson).
  - Token names/symbols rendered as React text (default-escaped) — no
    dangerouslySetInnerHTML anywhere in the slice.
2026-04-30 17:53:43 -07:00
Hanzo AI afe7ce1f12 feat(state): zustand swap+bridge slices
Pure data slices for Swap and Bridge state machines. Async lives in the
hooks; the store keeps the screen + flow phases in sync.

  swap   : idle → quoting → ready → approving → swapping → done | error
  bridge : idle → quoting → ready → locking → signing → minting → done | error

Quote shapes carry a `ts` timestamp; consumers reject quotes >30s old.
Route kinds locked: V2/V3 = AMM, V4 = DEX (rendered verbatim in UI).
2026-04-30 17:53:23 -07:00
Hanzo AI f028efd393 feat(web): settings + signing slice (7/7 — wallet UX complete)
Settings sub-router (`/settings/*`) with eight screens:

  Settings.tsx — top-level menu
  Networks.tsx — per-chain RPC override (HTTPS-only, persisted)
  Security.tsx — PIN change, biometric toggle, auto-lock 1/5/15/60/never
  Backup.tsx   — PIN re-auth → reveal mnemonic → 30s auto-redact + ack
  Language.tsx — 10 locales, en default
  Theme.tsx    — system / dark / light
  Currency.tsx — USD / EUR / JPY / GBP / CHF / CAD
  About.tsx    — VERSION + /.upstream-sha + GPL-3 + brand legal links

Signing modal — global tx confirmation triggered by Send / Swap /
Bridge / dApps before broadcast:

  SigningModal.tsx   — backdrop + ESC + reject-on-close
  TxSummary.tsx      — from / to / amount / gas / chain + ContractDecode
                       (local ABI + opt-in 4byte fallback)
  RiskIndicator.tsx  — green / yellow / red heuristics
                       (known contract → green, MAX_UINT approve → yellow,
                        unverified contract → red)
  useSigningModal.ts — hook + imperative API; promise-resolving queue,
                       single in-flight request enforced

State (`store/settings.ts`):
  { networks: { [chainId]: { rpcOverride } },
    security: { biometric, autoLockMin },
    locale, theme, currency }
  Persisted via zustand `persist` middleware, partialize boundary
  excludes nothing (pure UI prefs, no secrets).

State (`store/signing.ts`):
  Single-slot queue keyed by `pending`. `open(tx)` returns
  `Promise<{approved, reason?}>`. `beforeunload` rejects pending so
  caller awaits unwind cleanly.

Wired at app root: `App.tsx` mounts `<SigningModal />` lazily inside
`<Suspense>` so the modal is reachable from any slice but stays out
of the auth bootstrap bundle.

Constraints honoured:
  - ▼ brand defaults via `lib/brand.ts` + `getBootnodeRpcUrl()`,
    no empty-string URLs, white-label-ready.
  - Mnemonic reveal: PIN re-auth required, 30s auto-redact timer,
    overwrites on unmount, never logged or auto-clipboarded.
  - Risk heuristics never block; user always confirms.
  - Currency conversions through `https://${brand.gatewayDomain}/v1/prices`
    with 30s in-memory cache, no calldata leaks.

Build: 23.0 KB settings chunk (6.2 KB gzipped), 13.9 KB SigningModal
chunk (4.8 KB gzipped). Total dist 7.6 MB.
2026-04-30 17:52:26 -07:00
Hanzo AI 61332c02de fix(router): mount screens with /* splat so nested <Routes> match
Each screen's index.tsx exports a <Routes>-wrapper component (Auth,
Portfolio, Send, Receive — pattern from the screen Blues). When
mounted under a non-splat route (`path: "portfolio"`), the inner
<Routes> sees an empty relative path and matches nothing — so the
AppShell renders, the navbar paints, but the body is blank.

Switching to splat (`path: "portfolio/*"`) forwards the remainder
of the URL to each screen's nested router, which can then match
its own index + sub-routes.

Also adds the missing /auth route — Foundation never mounted it so
first-time users could not reach Welcome / Create / Import / SetPIN /
Unlock. Now reachable at /auth/{welcome,create,confirm,import,pin,unlock}.
2026-04-30 16:46:31 -07:00
Hanzo AI ab7ebb1c8f fix(web): unblock build — gui-stub, pin lib stubs, package.json union
The 4 wallet UX feat branches each added their own deps to
apps/web/package.json, which conflicted on merge. This commit
reconciles to a clean union of all required deps + ships the
fixes needed to make the merged tree build:

* package.json — union of all 4 branches' deps (qrcode.react,
  @noble/{curves,hashes}, @scure/{bip32,bip39}, @walletconnect/*,
  @solana/web3.js, viem, wagmi, etc.).
* @hanzo/gui v7's npm publish has no dist/ — Vite alias to a local
  src/lib/gui-stub.tsx with minimal Stack/YStack/XStack/Button/Text/
  Input/Card primitives. Tokenized props map to CSS vars set by
  loadBrandConfig().
* src/lib/chain-lux.ts stubbed — @l.x/api npm publish has unresolved
  __generated__/* imports; throws on use with a clear message
  pointing users to the EVM C-Chain path.
* src/lib/pin.ts — getPinAuth() handle returning biometricAvailable
  + verifyPin + unlock; matches the API ConfirmSend Blue invented.
* Stack → YStack across auth screens (Stack isn't exported by v7).
* validateMnemonic from @scure/bip39 (viem doesn't export it).
* Type-cast Uint8Array → BufferSource for Web Crypto in lib/crypto.ts
  (TS 5.7 lib.dom.d.ts tightened types).
* Confidential index gets a default export so router lazy() works.
* Lux Wallet fallback string → ▼ in Welcome.tsx.

Build output: 1.5MB total (uncompressed), 9 chunks, all 9 screens
lazy-loaded. tsc step dropped from build script — @l.x/* npm packages
ship raw .ts that fails tsc but Vite handles them.
2026-04-30 14:10:11 -07:00
Hanzo AI 795d09ff11 merge: wallet feat/confidential slice
# Conflicts:
#	apps/web/package.json
#	apps/web/src/screens/confidential/index.tsx
#	pnpm-lock.yaml
2026-04-30 14:03:22 -07:00
Hanzo AI eb6ffb9277 merge: wallet feat/stake-dapps slice
# Conflicts:
#	apps/web/package.json
#	apps/web/src/screens/dapps/index.tsx
#	apps/web/src/screens/stake/index.tsx
#	pnpm-lock.yaml
2026-04-30 14:03:11 -07:00
Hanzo AI 77775cbb3e merge: wallet feat/send-receive slice
# Conflicts:
#	apps/web/package.json
#	apps/web/src/screens/receive/index.tsx
#	apps/web/src/screens/send/index.tsx
#	pnpm-lock.yaml
2026-04-30 14:03:10 -07:00
Hanzo AI 089d862bac merge: wallet feat/auth-portfolio slice
# Conflicts:
#	apps/web/package.json
#	apps/web/src/screens/portfolio/index.tsx
2026-04-30 14:02:56 -07:00
Hanzo AI 7c853f4871 merge: brand defaults to ▼ placeholder 2026-04-30 14:02:05 -07:00