diff --git a/components/webln/index.js b/components/webln/index.js
index 4eed911c..27156b07 100644
--- a/components/webln/index.js
+++ b/components/webln/index.js
@@ -2,6 +2,7 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useState }
import { LNbitsProvider, useLNbits } from './lnbits'
import { NWCProvider, useNWC } from './nwc'
import { LNCProvider, useLNC } from './lnc'
+import lnbits from './lnbits2'
const WebLNContext = createContext({})
diff --git a/components/webln/index2.js b/components/webln/index2.js
new file mode 100644
index 00000000..5d8a0b86
--- /dev/null
+++ b/components/webln/index2.js
@@ -0,0 +1,22 @@
+import { createContext, useContext, useMemo, useState } from 'react'
+import lnbits from './lnbits2'
+
+const storageKey = 'webln:providers'
+
+const WebLNContext = createContext({})
+
+export function useWebLN () {
+ const { provider } = useContext(WebLNContext)
+ return provider
+}
+
+export function RawWebLNProvider ({ children }) {
+ const [provider, setProvider] = useState()
+
+ const value = useMemo(() => ({ provider, setProvider }), [])
+ return (
+
+ {children}
+
+ )
+}
diff --git a/components/webln/lnbits.js b/components/webln/lnbits.js
index 68eddcd9..b00a7763 100644
--- a/components/webln/lnbits.js
+++ b/components/webln/lnbits.js
@@ -67,8 +67,7 @@ const getPayment = async (baseUrl, adminKey, paymentHash) => {
export function LNbitsProvider ({ children }) {
const me = useMe()
- const [url, setUrl] = useState('')
- const [adminKey, setAdminKey] = useState('')
+ const [config, setConfig] = useState({})
const [status, setStatus] = useState()
const { logger } = useWalletLogger(Wallet.LNbits)
@@ -78,7 +77,7 @@ export function LNbitsProvider ({ children }) {
}
const getInfo = useCallback(async () => {
- const response = await getWallet(url, adminKey)
+ const response = await getWallet(config.url, config.adminKey)
return {
node: {
alias: response.name,
@@ -92,9 +91,11 @@ export function LNbitsProvider ({ children }) {
version: '1.0',
supports: ['lightning']
}
- }, [url, adminKey])
+ }, [config])
const sendPayment = useCallback(async (bolt11) => {
+ const { url, adminKey } = config
+
const hash = bolt11Tags(bolt11).payment_hash
logger.info('sending payment:', `payment_hash=${hash}`)
@@ -111,7 +112,7 @@ export function LNbitsProvider ({ children }) {
logger.error('payment failed:', `payment_hash=${hash}`, err.message || err.toString?.())
throw err
}
- }, [logger, url, adminKey])
+ }, [logger, config])
const loadConfig = useCallback(async () => {
let configStr = window.localStorage.getItem(storageKey)
@@ -129,20 +130,17 @@ export function LNbitsProvider ({ children }) {
}
const config = JSON.parse(configStr)
-
- const { url, adminKey } = config
- setUrl(url)
- setAdminKey(adminKey)
+ setConfig(config)
logger.info(
'loaded wallet config: ' +
'adminKey=****** ' +
- `url=${url}`)
+ `url=${config.url}`)
try {
// validate config by trying to fetch wallet
logger.info('trying to fetch wallet')
- await getWallet(url, adminKey)
+ await getWallet(config.url, config.adminKey)
logger.ok('wallet found')
setStatus(Status.Enabled)
logger.ok('wallet enabled')
@@ -156,8 +154,7 @@ export function LNbitsProvider ({ children }) {
const saveConfig = useCallback(async (config) => {
// immediately store config so it's not lost even if config is invalid
- setUrl(config.url)
- setAdminKey(config.adminKey)
+ setConfig(config)
// XXX This is insecure, XSS vulns could lead to loss of funds!
// -> check how mutiny encrypts their wallet and/or check if we can leverage web workers
@@ -186,8 +183,7 @@ export function LNbitsProvider ({ children }) {
const clearConfig = useCallback(() => {
window.localStorage.removeItem(storageKey)
- setUrl('')
- setAdminKey('')
+ setConfig({})
setStatus(undefined)
}, [])
@@ -196,8 +192,8 @@ export function LNbitsProvider ({ children }) {
}, [])
const value = useMemo(
- () => ({ name: 'LNbits', url, adminKey, status, saveConfig, clearConfig, getInfo, sendPayment }),
- [url, adminKey, status, saveConfig, clearConfig, getInfo, sendPayment])
+ () => ({ name: 'LNbits', ...config, status, saveConfig, clearConfig, getInfo, sendPayment }),
+ [config, status, saveConfig, clearConfig, getInfo, sendPayment])
return (
{children}
diff --git a/components/webln/lnbits2.js b/components/webln/lnbits2.js
new file mode 100644
index 00000000..c7b5e6c1
--- /dev/null
+++ b/components/webln/lnbits2.js
@@ -0,0 +1,112 @@
+import { bolt11Tags } from '@/lib/bolt11'
+
+export const name = 'LNbits'
+
+let config, logger
+
+export function setConfig (_config) {
+ config = _config
+}
+
+export function setLogger (_logger) {
+ logger = _logger
+}
+
+export async function getInfo () {
+ const response = await getWallet(config.url, config.adminKey)
+ return {
+ node: {
+ alias: response.name,
+ pubkey: ''
+ },
+ methods: [
+ 'getInfo',
+ 'getBalance',
+ 'sendPayment'
+ ],
+ version: '1.0',
+ supports: ['lightning']
+ }
+}
+
+export async function sendPayment (bolt11) {
+ const { url, adminKey } = config
+
+ const hash = bolt11Tags(bolt11).payment_hash
+ logger.info('sending payment:', `payment_hash=${hash}`)
+
+ try {
+ const response = await postPayment(url, adminKey, bolt11)
+
+ const checkResponse = await getPayment(url, adminKey, response.payment_hash)
+ if (!checkResponse.preimage) {
+ throw new Error('No preimage')
+ }
+
+ const preimage = checkResponse.preimage
+ logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
+ return { preimage }
+ } catch (err) {
+ logger.error('payment failed:', `payment_hash=${hash}`, err.message || err.toString?.())
+ throw err
+ }
+}
+
+const getWallet = async (baseUrl, adminKey) => {
+ const url = baseUrl.replace(/\/+$/, '')
+ const path = '/api/v1/wallet'
+
+ const headers = new Headers()
+ headers.append('Accept', 'application/json')
+ headers.append('Content-Type', 'application/json')
+ headers.append('X-Api-Key', adminKey)
+
+ const res = await fetch(url + path, { method: 'GET', headers })
+ if (!res.ok) {
+ const errBody = await res.json()
+ throw new Error(errBody.detail)
+ }
+
+ const wallet = await res.json()
+ return wallet
+}
+
+async function postPayment (baseUrl, adminKey, bolt11) {
+ const url = baseUrl.replace(/\/+$/, '')
+ const path = '/api/v1/payments'
+
+ const headers = new Headers()
+ headers.append('Accept', 'application/json')
+ headers.append('Content-Type', 'application/json')
+ headers.append('X-Api-Key', adminKey)
+
+ const body = JSON.stringify({ bolt11, out: true })
+
+ const res = await fetch(url + path, { method: 'POST', headers, body })
+ if (!res.ok) {
+ const errBody = await res.json()
+ throw new Error(errBody.detail)
+ }
+
+ const payment = await res.json()
+ return payment
+}
+
+async function getPayment (baseUrl, adminKey, paymentHash) {
+ const url = baseUrl.replace(/\/+$/, '')
+ const path = `/api/v1/payments/${paymentHash}`
+
+ const headers = new Headers()
+ headers.append('Accept', 'application/json')
+ headers.append('Content-Type', 'application/json')
+ headers.append('X-Api-Key', adminKey)
+
+ const res = await fetch(url + path, { method: 'GET', headers })
+ if (!res.ok) {
+ const errBody = await res.json()
+ throw new Error(errBody.detail)
+ }
+
+ const payment = await res.json()
+ return payment
+}