import { generateSecretKey, getPublicKey as getPubKeyFromSecret } from "nostr-tools/pure"; declare global { interface Window { nostr?: { getPublicKey(): Promise; signEvent(event: any): Promise; getRelays?(): Promise>; }; } } export type BunkerSignerInterface = { getPublicKey(): Promise; signEvent(event: any): Promise; close(): Promise; }; export function hasNostrExtension(): boolean { return typeof window !== "undefined" && !!window.nostr; } export async function getPublicKey(): Promise { if (!window.nostr) throw new Error("No Nostr extension found"); return window.nostr.getPublicKey(); } export async function signEvent(event: any): Promise { if (!window.nostr) throw new Error("No Nostr extension found"); return window.nostr.signEvent(event); } export function createAuthEvent(pubkey: string, challenge: string) { return { kind: 22242, created_at: Math.floor(Date.now() / 1000), tags: [ ["relay", ""], ["challenge", challenge], ], content: "", pubkey, }; } export function shortenPubkey(pubkey: string): string { if (!pubkey) return ""; return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`; } export interface NostrProfile { name?: string; picture?: string; about?: string; nip05?: string; displayName?: string; } const DEFAULT_RELAYS = [ "wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band", ]; export async function publishEvent(signedEvent: any): Promise { const { SimplePool } = await import("nostr-tools/pool"); let relayUrls: string[] = DEFAULT_RELAYS; try { if (window.nostr?.getRelays) { const ext = await window.nostr.getRelays(); const write = Object.entries(ext) .filter(([, p]) => (p as any).write) .map(([url]) => url); if (write.length > 0) relayUrls = write; } } catch {} const pool = new SimplePool(); try { await Promise.allSettled(pool.publish(relayUrls, signedEvent)); } finally { pool.close(relayUrls); } } export async function fetchNostrProfile( pubkey: string, relayUrls: string[] = DEFAULT_RELAYS ): Promise { const { SimplePool } = await import("nostr-tools/pool"); const pool = new SimplePool(); try { const event = await pool.get(relayUrls, { kinds: [0], authors: [pubkey], }); if (!event?.content) return {}; const meta = JSON.parse(event.content); return { name: meta.name || meta.display_name, displayName: meta.display_name, picture: meta.picture, about: meta.about, nip05: meta.nip05, }; } catch { return {}; } finally { pool.close(relayUrls); } } // NIP-46: External signer (bunker:// URI) export async function createBunkerSigner( input: string ): Promise<{ signer: BunkerSignerInterface; pubkey: string }> { const { BunkerSigner, parseBunkerInput } = await import("nostr-tools/nip46"); const bp = await parseBunkerInput(input); if (!bp) throw new Error("Invalid bunker URI or NIP-05 identifier"); const clientSecretKey = generateSecretKey(); const signer = BunkerSigner.fromBunker(clientSecretKey, bp); await signer.connect(); const pubkey = await signer.getPublicKey(); return { signer, pubkey }; } // NIP-46: Generate a nostrconnect:// URI for QR display export async function generateNostrConnectSetup( relayUrls: string[] = DEFAULT_RELAYS.slice(0, 2) ): Promise<{ uri: string; clientSecretKey: Uint8Array }> { const { createNostrConnectURI } = await import("nostr-tools/nip46"); const clientSecretKey = generateSecretKey(); const clientPubkey = getPubKeyFromSecret(clientSecretKey); const secretBytes = crypto.getRandomValues(new Uint8Array(8)); const secret = Array.from(secretBytes) .map((b) => b.toString(16).padStart(2, "0")) .join(""); const uri = createNostrConnectURI({ clientPubkey, relays: relayUrls, secret, name: "Belgian Bitcoin Embassy", }); return { uri, clientSecretKey }; } // NIP-46: Wait for a remote signer to connect via nostrconnect:// URI export async function waitForNostrConnectSigner( clientSecretKey: Uint8Array, uri: string, signal?: AbortSignal ): Promise<{ signer: BunkerSignerInterface; pubkey: string }> { const { BunkerSigner } = await import("nostr-tools/nip46"); const signer = await BunkerSigner.fromURI(clientSecretKey, uri, {}, signal); const pubkey = await signer.getPublicKey(); return { signer, pubkey }; }