Mirror of the live fix in hanzoai/gui pkgs/ai (@hanzo/ai) applied to this repo's pre-convergence frontend tree. Replaces the hanzo-node-backed crypto wallet — which couldn't create/show a wallet without a connected node — with a self-contained stack under apps/hanzo-desktop/src/lib/lux-wallet: - luxfi/crypto WASM (real FIPS-204 ML-DSA-65 + secp256k1 + keccak); EVM keys via BIP-44 keys.deriveSecp256k1 (bit-identical to ethers/MetaMask), PQ identity via keys.serviceIdentity. Secrets sealed in OS keychain, never plaintext. - create / import / list / reveal / balance / send across the 11 ready EVM chains (@luxwallet/chains + /rpc); non-EVM X/P/Q/Z marked "coming soon". - Sign-In-With-Wallet: /v1/iam/web3/nonce -> CAIP-122 -> EIP-191 sign -> /v1/iam/web3/verify (@luxwallet/connect + @luxfi/crypto). - local-HD-PQ default; MPC / Safe selectable (coming soon). Wiring: rewrite pages/crypto-wallet.tsx onto the store; node-independent /wallet route + "Sign in with Wallet" on quick-connection; drop dead wallets-context. MIT/BSD deps vendored under apps/hanzo-desktop/vendor. NOTE: this app now renders @hanzo/ai (main.tsx), so the shipping wallet fix lands via hanzoai/gui; this mirror keeps the repo's own tree consistent.
@luxfi/crypto
The literal luxfi/crypto Go code compiled to WebAssembly — not a
TypeScript reimplementation.
The .wasm shipped in this package is GOOS=js GOARCH=wasm go build of
github.com/luxfi/crypto +
cloudflare/circl. Every byte it
produces is wire-identical to the Lux on-chain precompiles and verifiers —
the same ML-DSA-65, secp256k1, keccak/sha3/shake, and X-Wing code that runs on
the chain, now callable from Node and the browser.
Install
npm install @luxfi/crypto
Usage
import { init } from "@luxfi/crypto";
const c = await init(); // loads + instantiates the wasm (idempotent)
// ML-DSA-65 (FIPS 204, NIST Level 3)
const { publicKey, secretKey } = c.mldsa65.keygen();
const sig = c.mldsa65.sign(secretKey, new TextEncoder().encode("hello"));
c.mldsa65.verify(publicKey, new TextEncoder().encode("hello"), sig); // true
// secp256k1 (Ethereum-compatible recoverable ECDSA)
const pub = c.secp256k1.getPublicKey(sk); // 65-byte uncompressed
const s = c.secp256k1.sign(sk, digest32); // 65-byte [R||S||V]
const rec = c.secp256k1.recover(digest32, s); // == pub
// Hashes
c.keccak256(bytes); // 32 bytes
c.sha3_256(bytes); // 32 bytes (FIPS 202)
c.shake256(bytes, 64); // arbitrary length
// X-Wing KEM (ML-KEM-768 + X25519), draft-connolly-cfrg-xwing-kem
const kp = c.xwing.keygen(); // pk 1216 / sk 32
const { sharedSecret, ciphertext } = c.xwing.encapsulate(kp.publicKey);
c.xwing.decapsulate(kp.secretKey, ciphertext); // == sharedSecret
// luxfi/keys derivation (BIP-39/BIP-32 over the same luxfi primitives)
c.keys.serviceIdentity(mnemonic, "hanzo/kms-operator"); // ML-DSA-65 NodeID
c.keys.deriveSecp256k1(mnemonic, 0, 0); // m/44'/60'/0'/0/0
Browser
The wasm is auto-resolved next to the module in Node. In the browser, pass it explicitly to avoid bundler resolution quirks:
await init(fetch(new URL("@luxfi/crypto/crypto.wasm", import.meta.url)));
init() also accepts a BufferSource, a Response/Promise<Response>, a
URL, or a filesystem path string.
API surface
| Group | Methods |
|---|---|
mldsa65 |
keygen(), keygenFromSeed(seed32), sign(sk, msg, ctx?, {deterministic?}), verify(pk, msg, sig, ctx?) |
secp256k1 |
sign(sk, digest32), recover(digest32, sig65), getPublicKey(sk, compressed?), verify(pk, digest32, sig64) |
| hashes | keccak256(data), sha3_256(data), shake256(data, outLen?) |
xwing |
keygen(), keygenFromSeed(seed32), encapsulate(pk, seed?), decapsulate(sk, ct) |
keys |
serviceIdentity(mnemonic, servicePath), deriveSecp256k1(mnemonic, account?, index?) |
All byte arguments and results are Uint8Array. Sizes: ML-DSA-65 pk 1952 / sk
4032 / sig 3309; X-Wing pk 1216 / sk 32 / ct 1120 / shared 32; secp256k1 sig 65.
Why standard Go and not TinyGo
The binary is built with the standard Go toolchain (GOOS=js GOARCH=wasm),
not TinyGo. TinyGo cannot build the dependency graph: luxfi/crypto/mldsa
pulls in luxfi/accel (GPU dispatch) whose cgo #cgo build-constraint lines
are "not implemented" by TinyGo, and circl's ML-DSA/ML-KEM use stdlib paths
TinyGo's runtime does not fully support. Standard Go excludes the cgo paths
under CGO_ENABLED=0 and falls back to the pure-Go circl implementation — the
same code the chain runs. The result is larger but wire-identical:
| Toolchain | Result |
|---|---|
TinyGo -target=wasm |
✗ fails (luxfi/accel cgo #cgo lines unsupported) |
Go GOOS=js GOARCH=wasm |
✓ crypto.wasm ≈ 7.2 MB (2.33 MB gzipped) |
License
SEE LICENSE IN LICENSE — Lux Ecosystem License.
Pure-Go cryptography from github.com/luxfi/crypto, cloudflare/circl, and
decred/dcrd/secp256k1.