SkateSpots/src/lib/nip06.ts
shakespeare.diy e29d35f1e4 fix: restore full skate-spots source tree from misplaced src/src/ copy
The entire skate-spots codebase was accidentally copied into src/src/ and
public/public/ instead of src/ and public/ directly. This left the real
src/ directory as blank MKStack template stubs, explaining why the landing
page showed the default template instead of SkateSpots.

Changes made:
- Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx,
  all pages, components, hooks, contexts, lib, i18n, test)
- Copied all files from public/public/ up to public/ (images, manifest, etc.)
- Deleted src/src/ and public/public/ nested directories
- Removed HelloWorld.tsx template stub from pages/
- Retained the previously fixed index.css (Tailwind v3 directives)

Full skate-spots feature set is now active:
- All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.)
- NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler
- Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.)
- Outfit Variable font, Amber pre-check in main.tsx
- All hooks (useSkateSpots, useCoaches, useZaps, etc.)
- i18n support (en/es)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 22:18:28 +07:00

107 lines
3 KiB
TypeScript

import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/english';
import { HDKey } from '@scure/bip32';
/**
* NIP-06: Basic key derivation from mnemonic seed phrase
* https://github.com/nostr-protocol/nips/blob/master/06.md
*
* Uses BIP39 for mnemonic generation and BIP32 for key derivation
* Derivation path: m/44'/1237'/account'/0/0
*/
export const NIP06_PATH = "m/44'/1237'/0'/0/0";
/**
* Generate a new mnemonic seed phrase
* @param strength - 128 for 12 words, 256 for 24 words (default: 128)
* @returns 12 or 24 word mnemonic phrase
*/
export function generateMnemonicPhrase(strength: 128 | 256 = 128): string {
return generateMnemonic(wordlist, strength);
}
/**
* Validate a mnemonic seed phrase
* @param mnemonic - Space-separated words
* @returns true if valid, false otherwise
*/
export function validateMnemonicPhrase(mnemonic: string): boolean {
try {
return validateMnemonic(mnemonic, wordlist);
} catch {
return false;
}
}
/**
* Derive a private key from a mnemonic phrase using NIP-06
* @param mnemonic - Space-separated mnemonic phrase
* @param account - Account index (default: 0)
* @returns 32-byte private key as Uint8Array
*/
export function privateKeyFromMnemonic(mnemonic: string, account: number = 0): Uint8Array {
if (!validateMnemonicPhrase(mnemonic)) {
throw new Error('Invalid mnemonic phrase');
}
// Generate seed from mnemonic
const seed = mnemonicToSeedSync(mnemonic);
// Derive key using NIP-06 path
const path = account === 0 ? NIP06_PATH : `m/44'/1237'/${account}'/0/0`;
const hdKey = HDKey.fromMasterSeed(seed);
const derivedKey = hdKey.derive(path);
if (!derivedKey.privateKey) {
throw new Error('Failed to derive private key');
}
return derivedKey.privateKey;
}
/**
* Derive multiple accounts from a single mnemonic
* @param mnemonic - Space-separated mnemonic phrase
* @param count - Number of accounts to derive (default: 1)
* @returns Array of 32-byte private keys
*/
export function deriveMultipleAccounts(mnemonic: string, count: number = 1): Uint8Array[] {
const accounts: Uint8Array[] = [];
for (let i = 0; i < count; i++) {
accounts.push(privateKeyFromMnemonic(mnemonic, i));
}
return accounts;
}
/**
* Normalize mnemonic phrase (trim, lowercase, normalize whitespace)
* @param mnemonic - Raw mnemonic input
* @returns Normalized mnemonic
*/
export function normalizeMnemonic(mnemonic: string): string {
return mnemonic
.trim()
.toLowerCase()
.replace(/\s+/g, ' '); // Normalize whitespace to single spaces
}
/**
* Split mnemonic into word array
* @param mnemonic - Space-separated mnemonic phrase
* @returns Array of words
*/
export function mnemonicToWords(mnemonic: string): string[] {
return normalizeMnemonic(mnemonic).split(' ');
}
/**
* Join word array into mnemonic string
* @param words - Array of mnemonic words
* @returns Space-separated mnemonic phrase
*/
export function wordsToMnemonic(words: string[]): string {
return words.join(' ');
}