import type { EventTemplate, Event } from 'nostr-tools' import { finalizeEvent } from 'nostr-tools' import { hexToBytes } from 'nostr-tools/utils' import { nostrAuthService } from './nostrAuth' import { nostrService } from './nostr' /** * Remote signer using Alby (NIP-07) * Alby exposes window.nostr API for signing events */ export class NostrRemoteSigner { /** * Sign an event template using Alby (window.nostr) */ private buildUnsignedEvent(eventTemplate: EventTemplate, pubkey: string): EventTemplate & { pubkey: string } { return { pubkey, ...eventTemplate, created_at: eventTemplate.created_at ?? Math.floor(Date.now() / 1000), } } private async signWithAlby(unsignedEvent: EventTemplate & { pubkey: string }): Promise { if (typeof window === 'undefined' || !window.nostr) { return null } try { const signedEvent = await window.nostr.signEvent({ kind: unsignedEvent.kind, created_at: unsignedEvent.created_at, tags: unsignedEvent.tags, content: unsignedEvent.content, }) return signedEvent as Event } catch (e) { console.error('Error signing with Alby:', e) throw new Error('Failed to sign event with Alby extension') } } private signWithPrivateKey(unsignedEvent: EventTemplate & { pubkey: string }): Event { const privateKey = nostrService.getPrivateKey() if (!privateKey) { throw new Error('Alby extension required for signing. Please install and connect Alby browser extension.') } const secretKey = hexToBytes(privateKey) return finalizeEvent(unsignedEvent, secretKey) } async signEvent(eventTemplate: EventTemplate): Promise { const pubkey = nostrService.getPublicKey() if (!pubkey) { throw new Error('Public key required for signing. Please connect with Alby.') } const unsignedEvent = this.buildUnsignedEvent(eventTemplate, pubkey) const albySigned = await this.signWithAlby(unsignedEvent) if (albySigned) { return albySigned } return this.signWithPrivateKey(unsignedEvent) } /** * Check if remote signing is available */ isAvailable(): boolean { const state = nostrAuthService.getState() return state.connected && !!state.pubkey } /** * Check if Alby is available */ isAlbyAvailable(): boolean { return typeof window !== 'undefined' && typeof window.nostr !== 'undefined' } } export const nostrRemoteSigner = new NostrRemoteSigner()