stacker.news/walletd/index.js

120 lines
3.1 KiB
JavaScript

const { PrismaClient } = require('@prisma/client')
const { authenticatedLndGrpc, subscribeToInvoices, getInvoice, getPayment } = require('ln-service')
const dotenv = require('dotenv')
const serialize = require('../api/resolvers/serial')
dotenv.config({ path: '..' })
const { lnd } = authenticatedLndGrpc({
cert: process.env.LND_CERT,
macaroon: process.env.LND_MACAROON,
socket: process.env.LND_SOCKET
})
const models = new PrismaClient()
async function recordInvoiceStatus (inv) {
console.log(inv)
if (inv.is_confirmed) {
await serialize(models,
models.$executeRaw`SELECT confirm_invoice(${inv.id}, ${Number(inv.received_mtokens)})`)
} else if (inv.is_canceled) {
// mark as cancelled
await serialize(models,
models.invoice.update({
where: {
hash: inv.id
},
data: {
cancelled: true
}
}))
}
}
// 1. subscribe to all invoices async
const sub = subscribeToInvoices({ lnd })
sub.on('invoice_updated', recordInvoiceStatus)
// 2. check all pending invoices from db in lnd
async function checkPendingInvoices () {
// invoices
const now = new Date()
const active = await models.invoice.findMany({
where: {
expiresAt: {
gt: now
},
cancelled: false,
confirmedAt: {
equals: null
}
}
})
active.forEach(async invoice => {
try {
const inv = await getInvoice({ id: invoice.hash, lnd })
await recordInvoiceStatus(inv)
} catch (error) {
console.log(invoice, error)
process.exit(1)
}
})
}
async function recordWithdrawlStatus (id, wdrwl) {
console.log(wdrwl)
if (wdrwl.is_confirmed) {
// mtokens also contains the fee?
// is this true for getPayment?
const fee = Number(wdrwl.payment.fee_mtokens)
const paid = Number(wdrwl.mtokens) - fee
await serialize(models, models.$executeRaw`
SELECT confirm_withdrawl(${id}, ${paid}, ${fee})`)
} else if (wdrwl.is_failed) {
let status = 'UNKNOWN_FAILURE'
if (wdrwl.failed.is_insufficient_balance) {
status = 'INSUFFICIENT_BALANCE'
} else if (wdrwl.failed.is_invalid_payment) {
status = 'INVALID_PAYMENT'
} else if (wdrwl.failed.is_pathfinding_timeout) {
status = 'PATHFINDING_TIMEOUT'
} else if (wdrwl.failed.is_route_not_found) {
status = 'ROUTE_NOT_FOUND'
}
await serialize(models, models.$executeRaw`
SELECT reverse_withdrawl(${id}, ${status})`)
}
}
async function checkPendingWithdrawls () {
// look for withdrawls that are 30 seconds old but don't have a status
const leftovers = await models.withdrawl.findMany({
where: {
createdAt: {
lt: new Date(new Date().setSeconds(new Date().getSeconds() - 30))
},
status: {
equals: null
}
}
})
leftovers.forEach(async withdrawl => {
try {
const wdrwl = await getPayment({ id: withdrawl.hash, lnd })
await recordWithdrawlStatus(withdrawl.id, wdrwl)
} catch (error) {
console.log(withdrawl, error)
process.exit(1)
}
})
// check withdrawls every 5 seconds
setTimeout(checkPendingWithdrawls, 5000)
}
checkPendingInvoices()
checkPendingWithdrawls()