import { getInvoice } from 'ln-service' import { Relay, signId, calculateId, getPublicKey } from 'nostr' const nostrOptions = { startAfter: 5, retryLimit: 21, retryBackoff: true } export async function nip57 ({ data: { hash }, boss, lnd, models }) { let inv, lnInv try { lnInv = await getInvoice({ id: hash, lnd }) inv = await models.invoice.findUnique({ where: { hash } }) } catch (err) { console.log(err) // on lnd related errors, we manually retry which so we don't exponentially backoff await boss.send('nip57', { hash }, nostrOptions) return } // check if invoice still exists since HODL invoices get deleted after usage if (!inv) return try { // if parsing fails it's not a zap console.log('zapping', inv.desc) const desc = JSON.parse(inv.desc) const ptag = desc.tags.filter(t => t?.length >= 2 && t[0] === 'p')[0] const etag = desc.tags.filter(t => t?.length >= 2 && t[0] === 'e')[0] const atag = desc.tags.filter(t => t?.length >= 2 && t[0] === 'a')[0] const relays = desc.tags.find(t => t?.length >= 2 && t[0] === 'relays').slice(1) const tags = [ptag] if (etag) tags.push(etag) if (atag) tags.push(atag) tags.push(['bolt11', lnInv.request]) tags.push(['description', inv.desc]) tags.push(['preimage', lnInv.secret]) const e = { kind: 9735, pubkey: getPublicKey(process.env.NOSTR_PRIVATE_KEY), created_at: Math.floor(new Date(lnInv.confirmed_at).getTime() / 1000), content: '', tags } e.id = await calculateId(e) e.sig = await signId(process.env.NOSTR_PRIVATE_KEY, e.id) console.log('zap note', e, relays) await Promise.allSettled( relays.map(r => new Promise((resolve, reject) => { const timeout = 1000 const relay = Relay(r) function timedout () { relay.close() console.log('failed to send to', r) reject(new Error('relay timeout')) } let timer = setTimeout(timedout, timeout) relay.on('open', () => { clearTimeout(timer) timer = setTimeout(timedout, timeout) relay.send(['EVENT', e]) }) relay.on('ok', () => { clearTimeout(timer) relay.close() console.log('sent zap to', r) resolve() }) }))) } catch (e) { console.log(e) } }