This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Nahualli is a TypeScript library for grinding vanity stealth key pairs for ERC-5564 stealth addresses. It brute-forces secp256k1 key generation until finding a compressed public key whose X coordinate matches a desired hex prefix. Parallelized via Web Workers with a main-thread fallback.
npm run build # Build with tsdown (ESM, minified, DTS)
npm run dev # Build in watch mode
npm test # Run all tests (vitest run)
npx vitest run tests/grinder.test.ts # Run a single test file
npm run typecheck # tsc --noEmit
npm run lint # Biome lint + format check
npm run format # Biome lint + format with auto-fix
npm run clean # Remove dist/Two entry points exported from package.json: ./ (main API) and ./worker (Web Worker code).
Core flow: session.ts orchestrates everything. It validates the pattern via matcher.ts, spawns a worker-pool.ts of N Web Workers (each running worker.ts -> grinder.ts), and exposes an observable state machine (subscribe()) that emits GrindingSessionState snapshots as workers report progress. First worker to find a match wins; all others are stopped.
Module roles:
grinder.ts-- the inner loop: generates random secp256k1 keys viakeys.ts, checks X coordinate prefix viamatcher.ts, returns on match or after batch exhaustionworker.ts-- Web Worker entry point, runsgrindBatchin an async loop with cooperative yielding (setTimeout(0))worker-pool.ts-- spawns/manages N workers, aggregates progress, routes resultssession.ts-- publicGrindingSessioninterface withstart()/stop()/getState()/subscribe(), observer pattern for state updates, falls back to main-thread grinding whenWorkeris unavailablegenerate.ts-- convenience wrapper: generates a random spending + viewing key pair without pattern matchingmatcher.ts-- hex pattern validation and prefix matching (skips parity byte of compressed pubkey)keys.ts-- thin wrapper around@noble/curves/secp256k1for key generationhex.ts--bytesToHex/hexToBytesutilitiestypes.ts-- shared type definitions (StealthKeyPair,StealthKeyResult,GrindingSessionState, worker protocol types)constants.ts-- default batch size and other shared constants
Worker protocol: Main thread sends WorkerCommand ("start" | "stop"), workers respond with WorkerMessage ("progress" | "result" | "error"). Uint8Arrays are serialized as number[] across the message boundary.
State machine: idle -> running -> (found | stopped | error)
- Only the X coordinate of the compressed public key is matched (parity byte
02/03is skipped) DEFAULT_BATCH_SIZE = 1000keys per worker iteration- Pattern difficulty is exponential:
16^Nexpected attempts for N hex chars @noble/curvesis a peer dependency, never bundled (seetsdown.config.ts)- The library targets both browser and Node.js; tests run in Node where
Workeris unavailable, exercising the main-thread fallback path - State snapshots deep-clone
Uint8Arraykey buffers so subscribers get isolated copies - Subscriber errors are caught per-listener to prevent one bad subscriber from breaking others
start()does not re-throw after setting error state; callers should usegetState()orsubscribe()to observe errors
Tests use Vitest with no mocks. Probabilistic tests use short patterns (1-2 hex chars) to guarantee success within bounded attempts. E2E tests verify cryptographic validity (private key recovers public key) and pattern matching on the final result.