stacker.news/api/resolvers/wallet.js

119 lines
3.9 KiB
JavaScript
Raw Normal View History

2021-05-13 01:51:37 +00:00
import { createInvoice, decodePaymentRequest, subscribeToPayViaRequest } from 'ln-service'
2021-05-11 15:52:50 +00:00
import { UserInputError, AuthenticationError } from 'apollo-server-micro'
2021-04-30 21:42:51 +00:00
export default {
Query: {
2021-05-06 21:15:22 +00:00
invoice: async (parent, { id }, { me, models, lnd }) => {
2021-05-11 15:52:50 +00:00
return await models.invoice.findUnique({ where: { id: Number(id) } })
2021-05-13 01:51:37 +00:00
},
withdrawl: async (parent, { id }, { me, models, lnd }) => {
return await models.withdrawl.findUnique({ where: { id: Number(id) } })
2021-04-30 21:42:51 +00:00
}
},
Mutation: {
2021-05-06 21:15:22 +00:00
createInvoice: async (parent, { amount }, { me, models, lnd }) => {
2021-05-11 15:52:50 +00:00
if (!me) {
throw new AuthenticationError('You must be logged in')
}
if (!amount || amount <= 0) {
throw new UserInputError('Amount must be positive', { argumentName: 'amount' })
}
// set expires at to 3 hours into future
const expiresAt = new Date(new Date().setHours(new Date().getHours() + 3))
const description = `${amount} sats for @${me.name} on stacker.news`
2021-05-13 01:51:37 +00:00
const invoice = await createInvoice({
description,
lnd,
tokens: amount,
expires_at: expiresAt
})
2021-05-11 15:52:50 +00:00
const data = {
hash: invoice.id,
bolt11: invoice.request,
expiresAt: expiresAt,
msatsRequested: amount * 1000,
user: {
connect: {
name: me.name
}
}
}
return await models.invoice.create({ data })
2021-05-12 23:04:19 +00:00
},
createWithdrawl: async (parent, { invoice, maxFee }, { me, models, lnd }) => {
if (!me) {
throw new AuthenticationError('You must be logged in')
}
// decode invoice to get amount
const decoded = await decodePaymentRequest({ lnd, request: invoice })
2021-05-13 21:19:51 +00:00
const msatsFee = Number(maxFee) * 1000
2021-05-12 23:04:19 +00:00
// create withdrawl transactionally (id, bolt11, amount, fee)
2021-05-13 21:19:51 +00:00
try {
const [withdrawl] =
2021-05-13 01:51:37 +00:00
await models.$queryRaw`SELECT * FROM create_withdrawl(${decoded.id}, ${invoice},
2021-05-13 21:19:51 +00:00
${Number(decoded.mtokens)}, ${msatsFee}, ${me.name})`
2021-05-12 23:04:19 +00:00
2021-05-13 21:19:51 +00:00
// create the payment, subscribing to its status
const sub = subscribeToPayViaRequest({
lnd,
request: invoice,
// can't use max_fee_mtokens https://github.com/alexbosworth/ln-service/issues/141
max_fee: Number(maxFee),
pathfinding_timeout: 30000
})
2021-05-12 23:04:19 +00:00
2021-05-13 21:19:51 +00:00
// if it's confirmed, update confirmed returning extra fees to user
sub.on('confirmed', async e => {
console.log(e)
// mtokens also contains the fee
const fee = Number(e.fee_mtokens)
const paid = Number(e.mtokens) - fee
2021-05-14 00:11:22 +00:00
await models.$queryRaw`
SELECT confirm_withdrawl(${withdrawl.id}, ${paid}, ${fee})`
2021-05-13 21:19:51 +00:00
})
2021-05-12 23:04:19 +00:00
2021-05-13 21:19:51 +00:00
// if the payment fails, we need to
// 1. return the funds to the user
// 2. update the widthdrawl as failed
sub.on('failed', async e => {
console.log(e)
let status = 'UNKNOWN_FAILURE'
if (e.is_insufficient_balance) {
status = 'INSUFFICIENT_BALANCE'
} else if (e.is_invalid_payment) {
status = 'INVALID_PAYMENT'
} else if (e.is_pathfinding_timeout) {
status = 'PATHFINDING_TIMEOUT'
} else if (e.is_route_not_found) {
status = 'ROUTE_NOT_FOUND'
}
2021-05-14 00:11:22 +00:00
await models.$queryRaw`
SELECT reverse_withdrawl(${withdrawl.id}, ${status})`
2021-05-13 21:19:51 +00:00
})
2021-05-12 23:04:19 +00:00
2021-05-13 21:19:51 +00:00
return withdrawl
} catch (error) {
const { meta: { message } } = error
if (message.includes('SN_INSUFFICIENT_FUNDS')) {
throw new UserInputError('insufficient funds')
}
throw error
}
2021-04-30 21:42:51 +00:00
}
2021-05-13 21:19:51 +00:00
},
Withdrawl: {
satsPaying: w => Math.floor(w.msatsPaying / 1000),
satsPaid: w => Math.floor(w.msatsPaid / 1000),
satsFeePaying: w => Math.floor(w.msatsFeePaying / 1000),
satsFeePaid: w => Math.floor(w.msatsFeePaid / 1000)
2021-04-30 21:42:51 +00:00
}
}