2024-06-03 17:41:15 -05:00
|
|
|
import { useCallback } from 'react'
|
|
|
|
import { useMe } from '@/components/me'
|
|
|
|
import useLocalState from '@/components/use-local-state'
|
2024-06-20 16:48:46 +02:00
|
|
|
import { useWalletLogger } from '@/components/wallet-logger'
|
2024-06-03 17:41:15 -05:00
|
|
|
import { SSR } from '@/lib/constants'
|
2024-06-20 19:56:37 +02:00
|
|
|
import { bolt11Tags } from '@/lib/bolt11'
|
2024-06-03 17:41:15 -05:00
|
|
|
|
|
|
|
// wallet definitions
|
|
|
|
export const WALLET_DEFS = [
|
2024-06-20 21:52:07 +02:00
|
|
|
await import('@/components/wallet/lnbits'),
|
|
|
|
await import('@/components/wallet/nwc')
|
2024-06-03 17:41:15 -05:00
|
|
|
]
|
|
|
|
|
|
|
|
export const Status = {
|
|
|
|
Initialized: 'Initialized',
|
|
|
|
Enabled: 'Enabled',
|
|
|
|
Locked: 'Locked',
|
|
|
|
Error: 'Error'
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useWallet (name) {
|
|
|
|
const me = useMe()
|
|
|
|
|
2024-06-20 19:56:37 +02:00
|
|
|
const wallet = name ? getWalletByName(name, me) : getEnabledWallet(me)
|
|
|
|
const { logger } = useWalletLogger(wallet)
|
2024-06-03 17:41:15 -05:00
|
|
|
const storageKey = getStorageKey(wallet?.name, me)
|
|
|
|
const [config, saveConfig, clearConfig] = useLocalState(storageKey)
|
|
|
|
|
|
|
|
const sendPayment = useCallback(async (bolt11) => {
|
2024-06-20 19:56:37 +02:00
|
|
|
const hash = bolt11Tags(bolt11).payment_hash
|
|
|
|
logger.info('sending payment:', `payment_hash=${hash}`)
|
|
|
|
try {
|
2024-06-21 22:41:54 +02:00
|
|
|
const { preimage } = await wallet.sendPayment({ bolt11, ...config, logger })
|
2024-06-20 19:56:37 +02:00
|
|
|
logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
|
|
|
|
} catch (err) {
|
|
|
|
const message = err.message || err.toString?.()
|
|
|
|
logger.error('payment failed:', `payment_hash=${hash}`, message)
|
|
|
|
throw err
|
|
|
|
}
|
2024-06-03 17:41:15 -05:00
|
|
|
}, [wallet, config, logger])
|
|
|
|
|
|
|
|
const enable = useCallback(() => {
|
|
|
|
enableWallet(name, me)
|
2024-06-20 19:56:37 +02:00
|
|
|
logger.ok('wallet enabled')
|
|
|
|
}, [name, me, logger])
|
|
|
|
|
|
|
|
const disable = useCallback(() => {
|
|
|
|
disableWallet(name, me)
|
2024-06-21 22:35:19 +02:00
|
|
|
logger.info('wallet disabled')
|
2024-06-20 19:56:37 +02:00
|
|
|
}, [name, me, logger])
|
2024-06-03 17:41:15 -05:00
|
|
|
|
2024-06-21 22:41:54 +02:00
|
|
|
const save = useCallback(async (config) => {
|
2024-06-20 20:34:09 +02:00
|
|
|
try {
|
2024-06-21 22:32:06 +02:00
|
|
|
// validate should log custom INFO and OK message
|
|
|
|
// TODO: add timeout
|
2024-06-21 22:41:54 +02:00
|
|
|
await wallet.validate({ logger, ...config })
|
|
|
|
saveConfig(config)
|
2024-06-20 20:34:09 +02:00
|
|
|
logger.ok('wallet attached')
|
|
|
|
} catch (err) {
|
2024-06-21 22:32:06 +02:00
|
|
|
const message = err.message || err.toString?.()
|
2024-06-20 20:34:09 +02:00
|
|
|
logger.error(message)
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}, [saveConfig, logger])
|
|
|
|
|
|
|
|
// delete is a reserved keyword
|
|
|
|
const delete_ = useCallback(() => {
|
|
|
|
try {
|
|
|
|
clearConfig()
|
|
|
|
logger.ok('wallet detached')
|
|
|
|
} catch (err) {
|
2024-06-21 22:32:06 +02:00
|
|
|
const message = err.message || err.toString?.()
|
2024-06-20 20:34:09 +02:00
|
|
|
logger.error(message)
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}, [clearConfig, logger])
|
|
|
|
|
2024-06-03 17:41:15 -05:00
|
|
|
return {
|
|
|
|
...wallet,
|
|
|
|
sendPayment,
|
|
|
|
config,
|
2024-06-20 20:34:09 +02:00
|
|
|
save,
|
|
|
|
delete: delete_,
|
2024-06-03 17:41:15 -05:00
|
|
|
enable,
|
2024-06-20 19:56:37 +02:00
|
|
|
disable,
|
|
|
|
isConfigured: !!config,
|
|
|
|
status: config?.enabled ? Status.Enabled : Status.Initialized,
|
2024-06-20 20:16:27 +02:00
|
|
|
canPay: !!wallet?.sendPayment,
|
|
|
|
canReceive: !!wallet?.createInvoice,
|
2024-06-20 19:56:37 +02:00
|
|
|
logger
|
2024-06-03 17:41:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getWalletByName (name, me) {
|
2024-06-20 19:56:37 +02:00
|
|
|
return WALLET_DEFS.find(def => def.name === name)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getEnabledWallet (me) {
|
|
|
|
// TODO: handle multiple enabled wallets
|
|
|
|
return WALLET_DEFS
|
2024-06-20 20:16:27 +02:00
|
|
|
.filter(def => !!def.sendPayment)
|
2024-06-20 19:56:37 +02:00
|
|
|
.find(def => {
|
2024-06-03 17:41:15 -05:00
|
|
|
const key = getStorageKey(def.name, me)
|
|
|
|
const config = SSR ? null : JSON.parse(window?.localStorage.getItem(key))
|
|
|
|
return config?.enabled
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function getStorageKey (name, me) {
|
|
|
|
let storageKey = `wallet:${name}`
|
|
|
|
if (me) {
|
|
|
|
storageKey = `${storageKey}:${me.id}`
|
|
|
|
}
|
|
|
|
return storageKey
|
|
|
|
}
|
|
|
|
|
|
|
|
function enableWallet (name, me) {
|
2024-06-20 19:56:37 +02:00
|
|
|
// mark all wallets as disabled except the one to enable
|
2024-06-03 17:41:15 -05:00
|
|
|
for (const walletDef of WALLET_DEFS) {
|
2024-06-21 22:18:16 +02:00
|
|
|
const key = getStorageKey(walletDef.name, me)
|
|
|
|
let config = JSON.parse(window.localStorage.getItem(key))
|
2024-06-03 17:41:15 -05:00
|
|
|
const toEnable = walletDef.name === name
|
2024-06-21 22:18:16 +02:00
|
|
|
if (config || toEnable) {
|
|
|
|
config = { ...config, enabled: toEnable }
|
2024-06-03 17:41:15 -05:00
|
|
|
window.localStorage.setItem(key, JSON.stringify(config))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-20 19:56:37 +02:00
|
|
|
|
|
|
|
function disableWallet (name, me) {
|
|
|
|
const key = getStorageKey(name, me)
|
|
|
|
const config = JSON.parse(window.localStorage.getItem(key))
|
|
|
|
if (!config) return
|
|
|
|
config.enabled = false
|
|
|
|
window.localStorage.setItem(key, JSON.stringify(config))
|
|
|
|
}
|