Compare commits
1 Commits
ef700e188a
...
6d3f7d4230
Author | SHA1 | Date | |
---|---|---|---|
|
6d3f7d4230 |
@ -30,7 +30,7 @@ export function useWallet (name) {
|
|||||||
const hash = bolt11Tags(bolt11).payment_hash
|
const hash = bolt11Tags(bolt11).payment_hash
|
||||||
logger.info('sending payment:', `payment_hash=${hash}`)
|
logger.info('sending payment:', `payment_hash=${hash}`)
|
||||||
try {
|
try {
|
||||||
const { preimage } = await wallet.sendPayment({ bolt11, ...config, logger })
|
const { preimage } = await wallet.sendPayment({ bolt11, config })
|
||||||
logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
|
logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err.message || err.toString?.()
|
const message = err.message || err.toString?.()
|
||||||
@ -39,6 +39,18 @@ export function useWallet (name) {
|
|||||||
}
|
}
|
||||||
}, [wallet, config, logger])
|
}, [wallet, config, logger])
|
||||||
|
|
||||||
|
const validate = useCallback(async (values) => {
|
||||||
|
try {
|
||||||
|
// validate should log custom INFO and OK message
|
||||||
|
// TODO: add timeout
|
||||||
|
return await wallet.validate({ logger, ...values })
|
||||||
|
} catch (err) {
|
||||||
|
const message = err.message || err.toString?.()
|
||||||
|
logger.error(message)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}, [wallet, logger])
|
||||||
|
|
||||||
const enable = useCallback(() => {
|
const enable = useCallback(() => {
|
||||||
enableWallet(name, me)
|
enableWallet(name, me)
|
||||||
logger.ok('wallet enabled')
|
logger.ok('wallet enabled')
|
||||||
@ -46,18 +58,15 @@ export function useWallet (name) {
|
|||||||
|
|
||||||
const disable = useCallback(() => {
|
const disable = useCallback(() => {
|
||||||
disableWallet(name, me)
|
disableWallet(name, me)
|
||||||
logger.info('wallet disabled')
|
logger.ok('wallet disabled')
|
||||||
}, [name, me, logger])
|
}, [name, me, logger])
|
||||||
|
|
||||||
const save = useCallback(async (config) => {
|
const save = useCallback((values) => {
|
||||||
try {
|
try {
|
||||||
// validate should log custom INFO and OK message
|
saveConfig(values)
|
||||||
// TODO: add timeout
|
|
||||||
await wallet.validate({ logger, ...config })
|
|
||||||
saveConfig(config)
|
|
||||||
logger.ok('wallet attached')
|
logger.ok('wallet attached')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err.message || err.toString?.()
|
const message = 'failed to attach: ' + err.message || err.toString?.()
|
||||||
logger.error(message)
|
logger.error(message)
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
@ -69,7 +78,7 @@ export function useWallet (name) {
|
|||||||
clearConfig()
|
clearConfig()
|
||||||
logger.ok('wallet detached')
|
logger.ok('wallet detached')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err.message || err.toString?.()
|
const message = 'failed to detach: ' + err.message || err.toString?.()
|
||||||
logger.error(message)
|
logger.error(message)
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
@ -78,6 +87,7 @@ export function useWallet (name) {
|
|||||||
return {
|
return {
|
||||||
...wallet,
|
...wallet,
|
||||||
sendPayment,
|
sendPayment,
|
||||||
|
validate,
|
||||||
config,
|
config,
|
||||||
save,
|
save,
|
||||||
delete: delete_,
|
delete: delete_,
|
||||||
@ -117,11 +127,11 @@ function getStorageKey (name, me) {
|
|||||||
function enableWallet (name, me) {
|
function enableWallet (name, me) {
|
||||||
// mark all wallets as disabled except the one to enable
|
// mark all wallets as disabled except the one to enable
|
||||||
for (const walletDef of WALLET_DEFS) {
|
for (const walletDef of WALLET_DEFS) {
|
||||||
const key = getStorageKey(walletDef.name, me)
|
|
||||||
let config = JSON.parse(window.localStorage.getItem(key))
|
|
||||||
const toEnable = walletDef.name === name
|
const toEnable = walletDef.name === name
|
||||||
if (config || toEnable) {
|
const key = getStorageKey(name, me)
|
||||||
config = { ...config, enabled: toEnable }
|
const config = JSON.parse(window.localStorage.getItem(key))
|
||||||
|
if (config.enabled || toEnable) {
|
||||||
|
config.enabled = toEnable
|
||||||
window.localStorage.setItem(key, JSON.stringify(config))
|
window.localStorage.setItem(key, JSON.stringify(config))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,8 @@ export const card = {
|
|||||||
badges: ['send only', 'non-custodialish']
|
badges: ['send only', 'non-custodialish']
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function validate ({ logger, url, adminKey }) {
|
export async function validate ({ logger, ...config }) {
|
||||||
logger.info('trying to fetch wallet')
|
return await getInfo({ logger, ...config })
|
||||||
await getWallet(url, adminKey)
|
|
||||||
logger.ok('wallet found')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const schema = object({
|
export const schema = object({
|
||||||
@ -53,7 +51,28 @@ export const schema = object({
|
|||||||
adminKey: string().length(32)
|
adminKey: string().length(32)
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function sendPayment ({ bolt11, url, adminKey }) {
|
async function getInfo ({ logger, ...config }) {
|
||||||
|
logger.info('trying to fetch wallet')
|
||||||
|
const response = await getWallet(config.url, config.adminKey)
|
||||||
|
logger.ok('wallet found')
|
||||||
|
return {
|
||||||
|
node: {
|
||||||
|
alias: response.name,
|
||||||
|
pubkey: ''
|
||||||
|
},
|
||||||
|
methods: [
|
||||||
|
'getInfo',
|
||||||
|
'getBalance',
|
||||||
|
'sendPayment'
|
||||||
|
],
|
||||||
|
version: '1.0',
|
||||||
|
supports: ['lightning']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendPayment ({ bolt11, config }) {
|
||||||
|
const { url, adminKey } = config
|
||||||
|
|
||||||
const response = await postPayment(url, adminKey, bolt11)
|
const response = await postPayment(url, adminKey, bolt11)
|
||||||
|
|
||||||
const checkResponse = await getPayment(url, adminKey, response.payment_hash)
|
const checkResponse = await getPayment(url, adminKey, response.payment_hash)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NOSTR_PUBKEY_HEX } from '@/lib/nostr'
|
import { NOSTR_PUBKEY_HEX } from '@/lib/nostr'
|
||||||
import { parseNwcUrl } from '@/lib/url'
|
import { parseNwcUrl } from '@/lib/url'
|
||||||
import { Relay, finalizeEvent, nip04 } from 'nostr-tools'
|
import { Relay } from 'nostr-tools'
|
||||||
import { object, string } from 'yup'
|
import { object, string } from 'yup'
|
||||||
|
|
||||||
export const name = 'nwc'
|
export const name = 'nwc'
|
||||||
@ -45,7 +45,11 @@ export const schema = object({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function validate ({ logger, nwcUrl }) {
|
export async function validate ({ logger, ...config }) {
|
||||||
|
return await getInfo({ logger, ...config })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getInfo ({ logger, nwcUrl }) {
|
||||||
const { relayUrl, walletPubkey } = parseNwcUrl(nwcUrl)
|
const { relayUrl, walletPubkey } = parseNwcUrl(nwcUrl)
|
||||||
|
|
||||||
logger.info(`requesting info event from ${relayUrl}`)
|
logger.info(`requesting info event from ${relayUrl}`)
|
||||||
@ -97,65 +101,3 @@ export async function validate ({ logger, nwcUrl }) {
|
|||||||
if (relay) logger.info(`closed connection to ${relayUrl}`)
|
if (relay) logger.info(`closed connection to ${relayUrl}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendPayment ({ bolt11, nwcUrl, logger }) {
|
|
||||||
const { relayUrl, walletPubkey, secret } = parseNwcUrl(nwcUrl)
|
|
||||||
|
|
||||||
const relay = await Relay.connect(relayUrl).catch(() => {
|
|
||||||
// NOTE: passed error is undefined for some reason
|
|
||||||
const msg = `failed to connect to ${relayUrl}`
|
|
||||||
logger.error(msg)
|
|
||||||
throw new Error(msg)
|
|
||||||
})
|
|
||||||
logger.ok(`connected to ${relayUrl}`)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ret = await new Promise(function (resolve, reject) {
|
|
||||||
(async function () {
|
|
||||||
const payload = {
|
|
||||||
method: 'pay_invoice',
|
|
||||||
params: { invoice: bolt11 }
|
|
||||||
}
|
|
||||||
const content = await nip04.encrypt(secret, walletPubkey, JSON.stringify(payload))
|
|
||||||
|
|
||||||
const request = finalizeEvent({
|
|
||||||
kind: 23194,
|
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
|
||||||
tags: [['p', walletPubkey]],
|
|
||||||
content
|
|
||||||
}, secret)
|
|
||||||
await relay.publish(request)
|
|
||||||
|
|
||||||
const filter = {
|
|
||||||
kinds: [23195],
|
|
||||||
authors: [walletPubkey],
|
|
||||||
'#e': [request.id]
|
|
||||||
}
|
|
||||||
relay.subscribe([filter], {
|
|
||||||
async onevent (response) {
|
|
||||||
try {
|
|
||||||
const content = JSON.parse(await nip04.decrypt(secret, walletPubkey, response.content))
|
|
||||||
if (content.error) return reject(new Error(content.error.message))
|
|
||||||
if (content.result) return resolve({ preimage: content.result.preimage })
|
|
||||||
} catch (err) {
|
|
||||||
return reject(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onclose (reason) {
|
|
||||||
if (!['closed by caller', 'relay connection closed by us'].includes(reason)) {
|
|
||||||
// only log if not closed by us (caller)
|
|
||||||
const msg = 'connection closed: ' + (reason || 'unknown reason')
|
|
||||||
logger.error(msg)
|
|
||||||
reject(new Error(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})().catch(reject)
|
|
||||||
})
|
|
||||||
return ret
|
|
||||||
} finally {
|
|
||||||
// For some reason, websocket is already in CLOSING or CLOSED state.
|
|
||||||
// relay?.close()
|
|
||||||
if (relay) logger.info(`closed connection to ${relayUrl}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -10,6 +10,7 @@ import { msatsToSats, numWithUnits, abbrNum, ensureB64, B64_URL_REGEX } from './
|
|||||||
import * as usersFragments from '@/fragments/users'
|
import * as usersFragments from '@/fragments/users'
|
||||||
import * as subsFragments from '@/fragments/subs'
|
import * as subsFragments from '@/fragments/subs'
|
||||||
import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon'
|
import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon'
|
||||||
|
import { parseNwcUrl } from './url'
|
||||||
import { datePivot } from './time'
|
import { datePivot } from './time'
|
||||||
import { decodeRune } from '@/lib/cln'
|
import { decodeRune } from '@/lib/cln'
|
||||||
import bip39Words from './bip39-words'
|
import bip39Words from './bip39-words'
|
||||||
|
@ -21,7 +21,9 @@ export default function WalletSettings () {
|
|||||||
...acc,
|
...acc,
|
||||||
[field.name]: wallet.config?.[field.name] || ''
|
[field.name]: wallet.config?.[field.name] || ''
|
||||||
}
|
}
|
||||||
}, {})
|
}, {
|
||||||
|
isDefault: wallet.isDefault || false
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenterLayout>
|
<CenterLayout>
|
||||||
@ -34,7 +36,8 @@ export default function WalletSettings () {
|
|||||||
onSubmit={async ({ enabled, ...values }) => {
|
onSubmit={async ({ enabled, ...values }) => {
|
||||||
try {
|
try {
|
||||||
const newConfig = !wallet.isConfigured
|
const newConfig = !wallet.isConfigured
|
||||||
await wallet.save(values)
|
await wallet.validate(values)
|
||||||
|
wallet.save(values)
|
||||||
// enable wallet if checkbox was set or if wallet was just configured
|
// enable wallet if checkbox was set or if wallet was just configured
|
||||||
if (enabled || newConfig) wallet.enable()
|
if (enabled || newConfig) wallet.enable()
|
||||||
else wallet.disable()
|
else wallet.disable()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user