import {Connector} from './Connector'; import {ConnectorConfig} from '../types/ConnectorConfig'; import { GetBalanceResponse, GetInfoResponse, KeysendArgs, LookupInvoiceArgs, LookupInvoiceResponse, MakeInvoiceResponse, RequestInvoiceArgs, SendPaymentResponse, SignMessageResponse, WebLNProvider, WebLNRequestMethod, } from '@webbtc/webln-types'; import type LNC from '@lightninglabs/lnc-web'; import {base64ToHex} from '../utils/base64ToHex'; // global instance of LNC let lnc: LNC; export async function getLNC() { try { if (lnc) { return lnc; } const LNC = (await import('@lightninglabs/lnc-web')).default; lnc = new LNC(); return lnc; } catch (error) { console.error(error); throw new Error('LNC is not available'); } } export {lnc}; // NOTE: as per NWC and other connectors - the user must put trust in the website to not use funds // without the user's permission. const lncPassword = 'ONLY CONNECT TO TRUSTED WEBSITES'; export class LNCConnector extends Connector { constructor(config: ConnectorConfig) { super(config); } override async init(): Promise { await getLNC(); const webln = new LNCWebLNProvider(lnc); try { const hasPreviouslyConnected = !lnc.credentials.pairingPhrase; if (hasPreviouslyConnected) { console.log('Pairing phrase does not exist'); lnc.credentials.password = lncPassword; } else { console.log('Pairing phrase set'); } await lnc.connect(); if (!hasPreviouslyConnected) { lnc.credentials.password = lncPassword; } while (!lnc.isConnected) { console.log('Waiting to connect...'); await new Promise((resolve) => { setTimeout(resolve, 100); }); } } catch (error) { console.error(error); lnc.disconnect(); lnc.credentials.clear(); throw error; } return webln; } override async unload(): Promise { lnc.disconnect(); lnc.credentials.clear(); await super.unload(); } } export class LNCWebLNProvider implements WebLNProvider { lnc: LNC; constructor(lnc: LNC) { this.lnc = lnc; } enable(): Promise { return Promise.resolve(); } async getInfo(): Promise { const data = await lnc.lnd.lightning.getInfo(); return { // TODO: support makeInvoice and webln.request methods: ['enable', 'getBalance', 'getInfo', 'sendPayment'], version: '1.0', node: { alias: data.alias, pubkey: data.identityPubkey, color: data.color, }, supports: ['lightning'], }; } makeInvoice( _args: string | number | RequestInvoiceArgs ): Promise { // TODO: implement (See Alby extension implementation) throw new Error('Method not implemented.'); } async sendPayment(paymentRequest: string): Promise { const data = await lnc.lnd.lightning.sendPaymentSync({ paymentRequest, }); if (data.paymentError) { throw new Error(data.paymentError); } if (!data.paymentPreimage) { throw new Error('No preimage in response'); } if (typeof data.paymentPreimage !== 'string') { throw new Error('expected preimage as string'); } return { preimage: base64ToHex(data.paymentPreimage), }; } async getBalance(): Promise { const balance = await lnc.lnd.lightning.channelBalance(); return { balance: parseInt(balance.localBalance?.sat || '0'), }; } keysend(_args: KeysendArgs): Promise { throw new Error('Method not implemented.'); } lnurl( _lnurl: string ): Promise<{status: 'OK'} | {status: 'ERROR'; reason: string}> { throw new Error('Method not implemented.'); } lookupInvoice(_args: LookupInvoiceArgs): Promise { throw new Error('Method not implemented.'); } request: | ((method: WebLNRequestMethod, args?: unknown) => Promise) | undefined; signMessage(_message: string): Promise { throw new Error('Method not implemented.'); } verifyMessage(_signature: string, _message: string): Promise { throw new Error('Method not implemented.'); } }