stacker.news/worker/nostr.js
ekzyis ac45fdc234
Use HODL invoices (#432)
* Use HODL invoices

* Fix expiry check comparing string with Date

* Fix unconfirmed user balance for HODL invoices

This is done by syncing the data from LND to the Invoice table.

If the columns is_held and msatsReceived are set, the frontend is told that we're ready to execute the action.

We then update the user balance in the same tx as the action.

We need to still keep checking the invoice for expiration though.

* Fix worker acting upon deleted invoices

* Prevent usage of invoice after expiration

* Use onComplete from <Countdown> to show expired status

* Remove unused lnd argument

* Fix item destructuring from query

* Fix balance added to every stacker

* Fix hmac required

* Fix invoices not used when logged in

* refactor: move invoiceable code into form

* renamed invoiceHash, invoiceHmac to hash, hmac since it's less verbose all over the place
* form now supports `invoiceable` in its props
* form then wraps `onSubmit` with `useInvoiceable` and passes optional invoice options

* Show expired if expired and canceled

* Also use useCallback for zapping

* Always expire modal invoices after 3m

* little styling thing

---------

Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2023-08-30 21:48:49 -05:00

89 lines
2.5 KiB
JavaScript

const { getInvoice } = require('ln-service')
const { Relay, signId, calculateId, getPublicKey } = require('nostr')
const nostrOptions = { startAfter: 5, retryLimit: 21, retryBackoff: true }
function nip57 ({ boss, lnd, models }) {
return async function ({ data: { hash } }) {
console.log('running nip57')
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 relays = desc.tags.find(t => t?.length >= 2 && t[0] === 'relays').slice(1)
const tags = [ptag]
if (etag) {
tags.push(etag)
}
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)
}
console.log('done running nip57')
}
}
module.exports = { nip57 }