Generate wallet resolver from fields

This commit is contained in:
ekzyis 2024-07-06 06:41:14 +02:00
parent 00f78daadc
commit ba00c3d9fa
4 changed files with 92 additions and 41 deletions

View File

@ -6,7 +6,7 @@ import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
import { SELECT, itemQueryWithMeta } from './item'
import { lnAddrOptions } from '@/lib/lnurl'
import { msatsToSats, msatsToSatsDecimal, ensureB64 } from '@/lib/format'
import { CLNAutowithdrawSchema, LNDAutowithdrawSchema, amountSchema, lnAddrAutowithdrawSchema, lnAddrSchema, ssValidate, withdrawlSchema } from '@/lib/validate'
import { CLNAutowithdrawSchema, amountSchema, lnAddrAutowithdrawSchema, lnAddrSchema, ssValidate, withdrawlSchema } from '@/lib/validate'
import { ANON_BALANCE_LIMIT_MSATS, ANON_INV_PENDING_LIMIT, USER_ID, BALANCE_LIMIT_MSATS, INVOICE_RETENTION_DAYS, INV_PENDING_LIMIT, USER_IDS_BALANCE_NO_LIMIT, Wallet } from '@/lib/constants'
import { datePivot } from '@/lib/time'
import assertGofacYourself from './ofac'
@ -14,6 +14,30 @@ import assertApiKeyNotPermitted from './apiKey'
import { createInvoice as createInvoiceCLN } from '@/lib/cln'
import { bolt11Tags } from '@/lib/bolt11'
import { checkInvoice } from 'worker/wallet'
import * as lnd from '@/components/wallet/lnd'
export const SERVER_WALLET_DEFS = [lnd]
function walletResolvers () {
const resolvers = {}
for (const {
schema,
server: { walletType, walletField, resolverName, testConnect }
} of SERVER_WALLET_DEFS) {
resolvers[resolverName] = async (parent, { settings, ...data }, { me, models }) => {
return await upsertWallet({
schema,
wallet: { field: walletField, type: walletType },
testConnect: (data) =>
testConnect(
data,
{ me, models, addWalletLog, lnService: { authenticatedLndGrpc, createInvoice } }
)
}, { settings, data }, { me, models })
}
}
return resolvers
}
export async function getInvoice (parent, { id }, { me, models, lnd }) {
const inv = await models.invoice.findUnique({
@ -424,41 +448,7 @@ export default {
}
return { id }
},
upsertWalletLND: async (parent, { settings, ...data }, { me, models }) => {
// make sure inputs are base64
data.macaroon = ensureB64(data.macaroon)
data.cert = ensureB64(data.cert)
const wallet = Wallet.LND
return await upsertWallet(
{
schema: LNDAutowithdrawSchema,
wallet,
testConnect: async ({ cert, macaroon, socket }) => {
try {
const { lnd } = await authenticatedLndGrpc({
cert,
macaroon,
socket
})
const inv = await createInvoice({
description: 'SN connection test',
lnd,
tokens: 0,
expires_at: new Date()
})
// we wrap both calls in one try/catch since connection attempts happen on RPC calls
await addWalletLog({ wallet, level: 'SUCCESS', message: 'connected to LND' }, { me, models })
return inv
} catch (err) {
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
const details = err[2]?.err?.details || err.message || err.toString?.()
throw new Error(details)
}
}
},
{ settings, data }, { me, models })
},
...walletResolvers(),
upsertWalletCLN: async (parent, { settings, ...data }, { me, models }) => {
data.cert = ensureB64(data.cert)

View File

@ -1,5 +1,5 @@
import { gql } from 'graphql-tag'
import { SERVER_WALLET_DEFS } from '@/components/wallet'
import { SERVER_WALLET_DEFS } from '@/api/resolvers/wallet'
function walletTypeDefs () {
const typeDefs = SERVER_WALLET_DEFS.map(

View File

@ -16,8 +16,6 @@ import { autowithdrawInitial } from '../autowithdraw-shared'
// wallet definitions
export const WALLET_DEFS = [lnbits, nwc, lnc, lnd]
export const SERVER_WALLET_DEFS = WALLET_DEFS.filter(w => w.server)
export const Status = {
Initialized: 'Initialized',
Enabled: 'Enabled',

View File

@ -1,3 +1,7 @@
import React from 'react'
import { ensureB64 } from '@/lib/format'
import { datePivot } from '@/lib/time'
import { LNDAutowithdrawSchema } from '@/lib/validate'
export const name = 'lnd'
@ -28,7 +32,13 @@ export const fields = [
label: 'cert',
type: 'text',
placeholder: 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNNVENDQWRpZ0F3SUJBZ0lRSHBFdFdrcGJwZHV4RVF2eVBPc3NWVEFLQmdncWhrak9QUVFEQWpBdk1SOHcKSFFZRFZRUUtFeFpzYm1RZ1lYVjBiMmRsYm1WeVlYUmxaQ0JqWlhKME1Rd3dDZ1lEVlFRREV3TmliMkl3SGhjTgpNalF3TVRBM01qQXhORE0wV2hjTk1qVXdNekF6TWpBeE5ETTBXakF2TVI4d0hRWURWUVFLRXhac2JtUWdZWFYwCmIyZGxibVZ5WVhSbFpDQmpaWEowTVF3d0NnWURWUVFERXdOaWIySXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak8KUFFNQkJ3TkNBQVJUS3NMVk5oZnhqb1FLVDlkVVdDbzUzSmQwTnBuL1BtYi9LUE02M1JxbU52dFYvdFk4NjJJZwpSbE41cmNHRnBEajhUeFc2OVhIK0pTcHpjWDdlN3N0Um80SFZNSUhTTUE0R0ExVWREd0VCL3dRRUF3SUNwREFUCkJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01CMEdBMVVkRGdRV0JCVDAKMnh3V25GeHRUNzI0MWxwZlNoNm9FWi9UMWpCN0JnTlZIUkVFZERCeWdnTmliMktDQ1d4dlkyRnNhRzl6ZElJRApZbTlpZ2d4d2IyeGhjaTF1TVMxaWIyS0NGR2h2YzNRdVpHOWphMlZ5TG1sdWRHVnlibUZzZ2dSMWJtbDRnZ3AxCmJtbDRjR0ZqYTJWMGdnZGlkV1pqYjI1dWh3Ui9BQUFCaHhBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaHdTc0VnQUQKTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUEwUTlkRXdoNXpPRnpwL3hYeHNpemh5SkxNVG5yazU1VWx1NHJPRwo4WW52QWlBVGt4U3p3Y3hZZnFscGx0UlNIbmd0NUJFcDBzcXlHL05nenBzb2pmMGNqQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K',
optional: <>optional if from <a href='https://en.wikipedia.org/wiki/Certificate_authority' target='_blank' rel='noreferrer'>CA</a> (e.g. voltage)</>,
// worker does not support JSX syntax
optional: React.createElement(
React.Fragment,
{},
'optional if from ',
React.createElement('a', { href: 'https://en.wikipedia.org/wiki/Certificate_authority', target: '_blank', rel: 'noreferrer' }, 'CA'),
' (e.g. voltage)'),
clear: true
}
]
@ -43,5 +53,58 @@ export const schema = LNDAutowithdrawSchema
export const server = {
walletType: 'LND',
resolverName: 'upsertWalletLND'
walletField: 'walletLND',
resolverName: 'upsertWalletLND',
testConnect: async (
{ cert, macaroon, socket },
{ me, models, addWalletLog, lnService: { authenticatedLndGrpc, createInvoice } }
) => {
try {
cert = ensureB64(cert)
macaroon = ensureB64(macaroon)
const { lnd } = await authenticatedLndGrpc({
cert,
macaroon,
socket
})
const inv = await createInvoice({
description: 'SN connection test',
lnd,
tokens: 0,
expires_at: new Date()
})
// we wrap both calls in one try/catch since connection attempts happen on RPC calls
await addWalletLog({ wallet: { type: 'LND' }, level: 'SUCCESS', message: 'connected to LND' }, { me, models })
return inv
} catch (err) {
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
const details = err[2]?.err?.details || err.message || err.toString?.()
throw new Error(details)
}
},
// TODO: use this instead of `autowithdrawLND` in worker/autowithdraw.js
createInvoice: async (
amount,
{ cert, macaroon, socket },
{ me, models, addWalletLog, lnService: { authenticatedLndGrpc, createInvoice } }
) => {
const { lnd } = await authenticatedLndGrpc({
cert,
macaroon,
socket
})
const invoice = await createInvoice({
description: me.hideInvoiceDesc ? undefined : 'autowithdraw to LND from SN',
lnd,
tokens: amount,
expires_at: datePivot(new Date(), { seconds: 360 })
})
return invoice.request
}
}