UNPKG

cyphertap

Version:

Nostr, Lightning and Ecash on a single Button component

207 lines (206 loc) 7.17 kB
// src/lib/api/cyphertap-api.svelte.ts import { NDKEvent } from '@nostr-dev-kit/ndk'; import { getEncodedTokenV4 } from '@cashu/cashu-ts'; // Import existing stores directly import { ndkInstance, currentUser, relayConnectionStatus, autoLogin } from '../stores/nostr.js'; import { wallet, walletBalance, isWalletReady } from '../stores/wallet.js'; import { get, derived } from 'svelte/store'; export class CyphertapAPI { static instance = null; // Create derived stores for reactive state _isLoggedIn = derived([currentUser, ndkInstance], ([$currentUser, $ndkInstance]) => Boolean($currentUser && $ndkInstance)); _isReady = derived([ndkInstance, currentUser, wallet, isWalletReady], ([$ndkInstance, $currentUser, $wallet, $isWalletReady]) => Boolean($ndkInstance && $currentUser && $wallet && $isWalletReady)); _npub = derived([currentUser], ([$currentUser]) => $currentUser?.npub || null); // Reactive state using runes that subscribe to derived stores #isLoggedIn = $state(false); #isReady = $state(false); #balance = $state(0); #npub = $state(null); constructor() { // Subscribe to derived stores and update runes this._isLoggedIn.subscribe(value => { this.#isLoggedIn = value; }); this._isReady.subscribe(value => { this.#isReady = value; }); walletBalance.subscribe(value => { this.#balance = value; }); this._npub.subscribe(value => { this.#npub = value; }); } // Public reactive getters get isLoggedIn() { return this.#isLoggedIn; } get isReady() { return this.#isReady; } get balance() { return this.#balance; } get npub() { return this.#npub; } static getInstance() { if (!CyphertapAPI.instance) { CyphertapAPI.instance = new CyphertapAPI(); } return CyphertapAPI.instance; } // Public API methods // User info getUserNpub() { return get(currentUser)?.npub || null; } getUserHex() { return get(currentUser)?.pubkey || null; } // Lightning operations async createLightningInvoice(amount, description = '') { const currentWallet = get(wallet); if (!currentWallet) throw new Error('Wallet not initialized'); try { const deposit = currentWallet.deposit(amount); const bolt11 = await deposit.start(); return { bolt11 // paymentHash: deposit.id || '' }; } catch (error) { throw new Error(`Invoice creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async sendLightningPayment(bolt11) { const currentWallet = get(wallet); if (!currentWallet) throw new Error('Wallet not initialized'); try { const result = await currentWallet.lnPay({ pr: bolt11 }, true); return { success: true, preimage: result?.preimage }; } catch (error) { throw new Error(`Payment failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Ecash operations async generateEcashToken(amount, memo = '') { const currentWallet = get(wallet); if (!currentWallet) throw new Error('Wallet not initialized'); try { const paymentInfo = { amount, unit: 'sat', paymentDescription: memo || `${amount} sats token` }; const result = await currentWallet.cashuPay(paymentInfo); const token = getEncodedTokenV4({ mint: result?.mint, proofs: result?.proofs, memo }); return { token, mint: result?.mint }; } catch (error) { throw new Error(`Token generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async receiveEcashToken(token) { const currentWallet = get(wallet); if (!currentWallet) throw new Error('Wallet not initialized'); try { const result = await currentWallet.receiveToken(token, 'Received via API'); return { success: true, amount: result?.amount || 0 }; } catch (error) { throw new Error(`Token receive failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Nostr operations async publishTextNote(content) { const ndk = get(ndkInstance); if (!ndk) throw new Error('NDK not initialized'); const event = new NDKEvent(ndk, { kind: 1, content, }); await event.publish(); return { id: event.id || '', pubkey: event.pubkey || '' }; } async publishEvent(event) { const ndk = get(ndkInstance); if (!ndk) throw new Error('NDK not initialized'); const ndkEvent = new NDKEvent(ndk, event); await ndkEvent.publish(); return { id: event.id || '', pubkey: event.pubkey || '' }; } subscribe(filter, callback) { const ndk = get(ndkInstance); if (!ndk) throw new Error('NDK not initialized'); const subscription = ndk.subscribe(filter); subscription.on('event', (event) => { callback({ id: event.id || '', pubkey: event.pubkey || '', content: event.content || '', kind: event.kind || 0, created_at: event.created_at || 0 }); }); return () => subscription.stop(); } async signEvent(event) { const ndk = get(ndkInstance); if (!ndk) throw new Error('NDK not initialized'); let ndkEvent = new NDKEvent(ndk, event); await ndkEvent.sign(); return { id: event.id || '', pubkey: event.pubkey || '', signature: event.sig || '' }; } // Encryption/Decryption async encrypt(content, recipientPubkey) { const ndk = get(ndkInstance); const user = get(currentUser); if (!ndk?.signer || !user) { throw new Error('Signer not available'); } return await ndk.signer.encrypt(user, content, recipientPubkey); } async decrypt(encryptedContent, senderPubkey) { const ndk = get(ndkInstance); if (!ndk?.signer) { throw new Error('Signer not available'); } return await ndk.signer.decrypt(encryptedContent, senderPubkey); } // Utility methods getConnectionStatus() { const status = get(relayConnectionStatus); const connected = status.filter(relay => relay.connected).length; return { connected, total: status.length }; } } // Export singleton instance export const cyphertap = CyphertapAPI.getInstance();