Merge branch 'master' into blinkreceiver

This commit is contained in:
ekzyis 2024-10-22 21:21:56 +02:00
commit 455f6ab665
26 changed files with 160 additions and 76 deletions

3
.gitignore vendored
View File

@ -56,3 +56,6 @@ docker-compose.*.yml
# nostr wallet connect
scripts/nwc-keys.json
# lnbits
docker/lnbits/data

View File

@ -2,11 +2,13 @@ import { cachedFetcher } from '@/lib/fetch'
import { toPositiveNumber } from '@/lib/validate'
import { authenticatedLndGrpc, getIdentity, getHeight, getWalletInfo, getNode } from 'ln-service'
const { lnd } = authenticatedLndGrpc({
const lnd = global.lnd || authenticatedLndGrpc({
cert: process.env.LND_CERT,
macaroon: process.env.LND_MACAROON,
socket: process.env.LND_SOCKET
})
}).lnd
if (process.env.NODE_ENV === 'development') global.lnd = lnd
// Check LND GRPC connection
getWalletInfo({ lnd }, (err, result) => {
@ -19,19 +21,24 @@ getWalletInfo({ lnd }, (err, result) => {
export async function estimateRouteFee ({ lnd, destination, tokens, mtokens, request, timeout }) {
return await new Promise((resolve, reject) => {
const params = {}
if (request) {
params.payment_request = request
} else {
params.dest = Buffer.from(destination, 'hex')
params.amt_sat = tokens ? toPositiveNumber(tokens) : toPositiveNumber(BigInt(mtokens) / BigInt(1e3))
}
lnd.router.estimateRouteFee({
dest: Buffer.from(destination, 'hex'),
amt_sat: tokens ? toPositiveNumber(tokens) : toPositiveNumber(BigInt(mtokens) / BigInt(1e3)),
payment_request: request,
...params,
timeout
}, (err, res) => {
if (err) {
reject(err)
return
}
if (res?.failure_reason) {
reject(new Error(`Unable to estimate route: ${res.failure_reason}`))
if (res?.failure_reason) {
reject(new Error(`Unable to estimate route: ${res.failure_reason}`))
} else {
reject(err)
}
return
}
@ -124,7 +131,7 @@ export const getBlockHeight = cachedFetcher(async function fetchBlockHeight ({ l
export const getOurPubkey = cachedFetcher(async function fetchOurPubkey ({ lnd, ...args }) {
try {
const { identity } = await getIdentity({ lnd, ...args })
const identity = await getIdentity({ lnd, ...args })
return identity.public_key
} catch (err) {
throw new Error(`Unable to fetch identity: ${err.message}`)

View File

@ -260,7 +260,7 @@ export default {
const { name } = data
await ssValidate(territorySchema, data, { models, me, sub: { name } })
await ssValidate(territorySchema, data, { models, me })
const oldSub = await models.sub.findUnique({ where: { name } })
if (!oldSub) {

View File

@ -482,12 +482,12 @@ const resolvers = {
FROM "Withdrawl"
WHERE "userId" = ${me.id}
AND id = ${Number(id)}
AND now() > created_at + interval '${retention}'
AND now() > created_at + ${retention}::INTERVAL
AND hash IS NOT NULL
AND status IS NOT NULL
), updated_rows AS (
UPDATE "Withdrawl"
SET hash = NULL, bolt11 = NULL
SET hash = NULL, bolt11 = NULL, preimage = NULL
FROM to_be_updated
WHERE "Withdrawl".id = to_be_updated.id)
SELECT * FROM to_be_updated;`
@ -499,7 +499,7 @@ const resolvers = {
console.error(error)
await models.withdrawl.update({
where: { id: invoice.id },
data: { hash: invoice.hash, bolt11: invoice.bolt11 }
data: { hash: invoice.hash, bolt11: invoice.bolt11, preimage: invoice.preimage }
})
throw new GqlInputError('failed to drop bolt11 from lnd')
}
@ -647,14 +647,21 @@ async function upsertWallet (
}
const { id, ...walletData } = data
const { autoWithdrawThreshold, autoWithdrawMaxFeePercent, enabled, priority } = settings
const {
autoWithdrawThreshold,
autoWithdrawMaxFeePercent,
autoWithdrawMaxFeeTotal,
enabled,
priority
} = settings
const txs = [
models.user.update({
where: { id: me.id },
data: {
autoWithdrawMaxFeePercent,
autoWithdrawThreshold
autoWithdrawThreshold,
autoWithdrawMaxFeeTotal
}
})
]

View File

@ -182,6 +182,7 @@ export default gql`
withdrawMaxFeeDefault: Int!
autoWithdrawThreshold: Int
autoWithdrawMaxFeePercent: Float
autoWithdrawMaxFeeTotal: Int
}
type UserOptional {

View File

@ -91,6 +91,7 @@ const typeDefs = `
input AutowithdrawSettings {
autoWithdrawThreshold: Int!
autoWithdrawMaxFeePercent: Float!
autoWithdrawMaxFeeTotal: Int!
priority: Int
enabled: Boolean
}

View File

@ -135,3 +135,5 @@ toyota-corolla0,pr,#1449,,good-first-issue,,,,20k,toyota_corolla0@stacker.news,2
toyota-corolla0,pr,#1455,#1437,good-first-issue,,,,20k,toyota_corolla0@stacker.news,2024-10-02
SouthKoreaLN,issue,#1436,,easy,,,,10k,south_korea_ln@stacker.news,2024-10-02
TonyGiorgio,issue,#1462,,easy,urgent,,,30k,TonyGiorgio@stacker.news,2024-10-07
hkarani,issue,#1369,#1458,good-first-issue,,,,2k,asterisk32@stacker.news,2024-10-21
toyota-corolla0,pr,#1369,#1458,good-first-issue,,,,20k,toyota_corolla0@stacker.news,2024-10-20

1 name type pr id issue ids difficulty priority changes requested notes amount receive method date paid
135 toyota-corolla0 pr #1455 #1437 good-first-issue 20k toyota_corolla0@stacker.news 2024-10-02
136 SouthKoreaLN issue #1436 easy 10k south_korea_ln@stacker.news 2024-10-02
137 TonyGiorgio issue #1462 easy urgent 30k TonyGiorgio@stacker.news 2024-10-07
138 hkarani issue #1369 #1458 good-first-issue 2k asterisk32@stacker.news 2024-10-21
139 toyota-corolla0 pr #1369 #1458 good-first-issue 20k toyota_corolla0@stacker.news 2024-10-20

View File

@ -4,6 +4,7 @@ import { useMe } from './me'
import { useEffect, useState } from 'react'
import { isNumber } from '@/lib/validate'
import { useIsClient } from './use-client'
import Link from 'next/link'
function autoWithdrawThreshold ({ me }) {
return isNumber(me?.privates?.autoWithdrawThreshold) ? me?.privates?.autoWithdrawThreshold : 10000
@ -12,7 +13,8 @@ function autoWithdrawThreshold ({ me }) {
export function autowithdrawInitial ({ me }) {
return {
autoWithdrawThreshold: autoWithdrawThreshold({ me }),
autoWithdrawMaxFeePercent: isNumber(me?.privates?.autoWithdrawMaxFeePercent) ? me?.privates?.autoWithdrawMaxFeePercent : 1
autoWithdrawMaxFeePercent: isNumber(me?.privates?.autoWithdrawMaxFeePercent) ? me?.privates?.autoWithdrawMaxFeePercent : 1,
autoWithdrawMaxFeeTotal: isNumber(me?.privates?.autoWithdrawMaxFeeTotal) ? me?.privates?.autoWithdrawMaxFeeTotal : 1
}
}
@ -51,13 +53,30 @@ export function AutowithdrawSettings ({ wallet }) {
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
required
/>
<h3 className='text-center text-muted pt-3'>network fees</h3>
<h6 className='text-center pb-3'>
we'll use whichever setting is higher during{' '}
<Link
target='_blank'
href='https://docs.lightning.engineering/the-lightning-network/pathfinding'
rel='noreferrer'
>pathfinding
</Link>
</h6>
<Input
label='max fee'
label='max fee rate'
name='autoWithdrawMaxFeePercent'
hint='max fee as percent of withdrawal amount'
append={<InputGroup.Text>%</InputGroup.Text>}
required
/>
<Input
label='max fee total'
name='autoWithdrawMaxFeeTotal'
hint='max fee for any withdrawal amount'
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
required
/>
</div>
</div>
</>

View File

@ -12,10 +12,7 @@ import useIndexedDB from './use-indexeddb'
import { SSR } from '@/lib/constants'
export function WalletLogs ({ wallet, embedded }) {
const { logs, setLogs, hasMore, loadMore, loadLogs, loading } = useWalletLogs(wallet)
useEffect(() => {
loadLogs()
}, [loadLogs])
const { logs, setLogs, hasMore, loadMore, loading } = useWalletLogs(wallet)
const showModal = useShowModal()
@ -46,7 +43,7 @@ export function WalletLogs ({ wallet, embedded }) {
? <div className='w-100 text-center'>loading...</div>
: logs.length === 0 && <div className='w-100 text-center'>empty</div>}
{hasMore
? <Button onClick={loadMore} size='sm' className='mt-3'>Load More</Button>
? <div className='w-100 text-center'><Button onClick={loadMore} size='sm' className='mt-3'>more</Button></div>
: <div className='w-100 text-center'>------ start of logs ------</div>}
</div>
</>
@ -228,7 +225,7 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
const loadMore = useCallback(async () => {
if (hasMore) {
setLoading(true)
const result = await loadLogsPage(page, logsPerPage, wallet)
const result = await loadLogsPage(page + 1, logsPerPage, wallet)
setLogs(prevLogs => [...prevLogs, ...result.data])
setHasMore(result.hasMore)
setTotal(result.total)
@ -247,5 +244,9 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
setLoading(false)
}, [wallet, loadLogsPage])
useEffect(() => {
loadLogs()
}, [wallet])
return { logs, hasMore, total, loadMore, loadLogs, setLogs, loading }
}

View File

@ -586,7 +586,8 @@ services:
- 'keys-file.json'
cpu_shares: "${CPU_SHARES_LOW}"
lnbits:
image: lnbits/lnbits:0.12.5
build:
context: ./docker/lnbits
container_name: lnbits
profiles:
- wallets
@ -596,6 +597,7 @@ services:
depends_on:
- stacker_lnd
environment:
- LNBITS_ADMIN_UI=true
- LNBITS_BACKEND_WALLET_CLASS=LndWallet
- LND_GRPC_ENDPOINT=stacker_lnd
- LND_GRPC_PORT=10009

5
docker/lnbits/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM lnbits/lnbits:0.12.5
COPY ["./data/database.sqlite3", "/app/data/database.sqlite3"]
COPY ["./data/.super_user", "/app/data/.super_user"]

View File

@ -0,0 +1 @@
e46288268b67457399a5fca81809573e

Binary file not shown.

View File

@ -1,4 +1,4 @@
FROM polarlightning/lnd:0.17.5-beta
FROM polarlightning/lnd:0.18.0-beta
ARG LN_NODE_FOR
ENV LN_NODE_FOR=$LN_NODE_FOR

View File

@ -27,6 +27,7 @@ ${STREAK_FIELDS}
noReferralLinks
fiatCurrency
autoWithdrawMaxFeePercent
autoWithdrawMaxFeeTotal
autoWithdrawThreshold
withdrawMaxFeeDefault
satsFilter

View File

@ -26,6 +26,10 @@ class LRUCache {
return value
}
delete (key) {
this.cache.delete(key)
}
set (key, value) {
if (this.cache.has(key)) this.cache.delete(key)
else if (this.cache.size >= this.maxSize) {

View File

@ -366,7 +366,8 @@ export function advSchema (args) {
export const autowithdrawSchemaMembers = {
enabled: boolean(),
autoWithdrawThreshold: intValidator.required('required').min(0, 'must be at least 0').max(msatsToSats(BALANCE_LIMIT_MSATS), `must be at most ${abbrNum(msatsToSats(BALANCE_LIMIT_MSATS))}`),
autoWithdrawMaxFeePercent: floatValidator.required('required').min(0, 'must be at least 0').max(50, 'must not exceed 50')
autoWithdrawMaxFeePercent: floatValidator.required('required').min(0, 'must be at least 0').max(50, 'must not exceed 50'),
autoWithdrawMaxFeeTotal: intValidator.required('required').min(0, 'must be at least 0').max(1_000, 'must not exceed 1000')
}
export const lnAddrAutowithdrawSchema = object({
@ -726,7 +727,8 @@ export const lnbitsSchema = object().shape({
test: invoiceKey => adminKey !== invoiceKey,
message: 'invoice key cannot be the same as admin key'
})
})
}),
...autowithdrawSchemaMembers
// need to set order to avoid cyclic dependencies in Yup schema
// see https://github.com/jquense/yup/issues/176#issuecomment-367352042
}, ['adminKey', 'invoiceKey'])
@ -745,7 +747,8 @@ export const nwcSchema = object().shape({
test: nwcUrlRecv => nwcUrlRecv !== nwcUrl,
message: 'connection for receiving cannot be the same as for sending'
})
})
}),
...autowithdrawSchemaMembers
}, ['nwcUrl', 'nwcUrlRecv'])
export const blinkSchema = object().shape({
@ -815,7 +818,8 @@ export const phoenixdSchema = object().shape({
test: secondary => primary !== secondary,
message: 'secondary password cannot be the same as primary password'
})
})
}),
...autowithdrawSchemaMembers
}, ['primaryPassword', 'secondaryPassword'])
export const bioSchema = object({

24
package-lock.json generated
View File

@ -7057,9 +7057,9 @@
}
},
"node_modules/bitcore-lib": {
"version": "8.25.40",
"resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.25.40.tgz",
"integrity": "sha512-mb6kvfhoiIdoyFsDlhIFVst3HpeGjGYBf0XDxTdZ+H07EC4JuiViA3bnQ5uZbZjHFngEl0GTPaoK1Zaolutw4A==",
"version": "8.25.47",
"resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.25.47.tgz",
"integrity": "sha512-qDZr42HuP4P02I8kMGZUx/vvwuDsz8X3rQxXLfM0BtKzlQBcbSM7ycDkDN99Xc5jzpd4fxNQyyFXOmc6owUsrQ==",
"dependencies": {
"bech32": "=2.0.0",
"bip-schnorr": "=0.6.4",
@ -15626,9 +15626,9 @@
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="
},
"node_modules/node-addon-api": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
"integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
},
"node_modules/node-fetch": {
"version": "2.7.0",
@ -18038,17 +18038,17 @@
"integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw=="
},
"node_modules/secp256k1": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz",
"integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz",
"integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==",
"hasInstallScript": true,
"dependencies": {
"elliptic": "^6.5.4",
"node-addon-api": "^2.0.0",
"elliptic": "^6.5.7",
"node-addon-api": "^5.0.0",
"node-gyp-build": "^4.2.0"
},
"engines": {
"node": ">=10.0.0"
"node": ">=18.0.0"
}
},
"node_modules/secure-json-parse": {

View File

@ -0,0 +1,8 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "autoWithdrawMaxFeeTotal" INTEGER;
-- set max total fee for users with autowithdrawals enabled to not interfere with them.
-- we set it to 0 instead of 1 because that preserves old behavior.
UPDATE "users"
SET "autoWithdrawMaxFeeTotal" = 0
WHERE "autoWithdrawMaxFeePercent" IS NOT NULL;

View File

@ -118,6 +118,7 @@ model User {
lnAddr String?
autoWithdrawMaxFeePercent Float?
autoWithdrawThreshold Int?
autoWithdrawMaxFeeTotal Int?
muters Mute[] @relation("muter")
muteds Mute[] @relation("muted")
ArcOut Arc[] @relation("fromUser")

View File

@ -125,7 +125,8 @@ function extractConfig (fields, config, client) {
const field = fields.find(({ name }) => name === key)
// filter server config which isn't specified as wallet fields
if (client && (key.startsWith('autoWithdraw') || key === 'id')) return acc
// (we allow autowithdraw members to pass validation)
if (client && key === 'id') return acc
// field might not exist because config.enabled doesn't map to a wallet field
if (!field || (client ? isClientField(field) : isServerField(field))) {
@ -198,6 +199,10 @@ function useConfig (wallet) {
if (transformedConfig) {
newClientConfig = Object.assign(newClientConfig, transformedConfig)
}
// these are stored on the server
delete newClientConfig.autoWithdrawMaxFeePercent
delete newClientConfig.autoWithdrawThreshold
delete newClientConfig.autoWithdrawMaxFeeTotal
} catch {
valid = false
}
@ -292,6 +297,7 @@ function useServerConfig (wallet) {
const saveConfig = useCallback(async ({
autoWithdrawThreshold,
autoWithdrawMaxFeePercent,
autoWithdrawMaxFeeTotal,
priority,
enabled,
...config
@ -306,6 +312,7 @@ function useServerConfig (wallet) {
settings: {
autoWithdrawThreshold: Number(autoWithdrawThreshold),
autoWithdrawMaxFeePercent: Number(autoWithdrawMaxFeePercent),
autoWithdrawMaxFeeTotal: Number(autoWithdrawMaxFeeTotal),
priority,
enabled
},

View File

@ -1,27 +1,24 @@
For testing LNbits, you need to create a LNbits account first via the web interface.
LNbits' database is seeded with a superuser (see https://docs.lnbits.org/guide/admin_ui.html).
By default, you can access it at `localhost:5001` (see `LNBITS_WEB_PORT` in .env.development).
The following credentials were used:
After you created a wallet, you should find the invoice and admin key under `Node URL, API keys and API docs`.
- username: `stackernews`
- password: `stackernews`
> [!IMPORTANT]
>
> Since your browser is running on your host machine but the server is running inside a docker container, the server will not be able to reach LNbits with `localhost:5001` to create invoices. This makes it hard to test send+receive at the same time.
>
> For now, you need to patch the `_createInvoice` function in wallets/lnbits/server.js to always use `lnbits:5000` as the URL:
>
> ```diff
> diff --git a/wallets/lnbits/server.js b/wallets/lnbits/server.js
> index 39949775..e3605c45 100644
> --- a/wallets/lnbits/server.js
> +++ b/wallets/lnbits/server.js
> @@ -11,6 +11,7 @@ async function _createInvoice ({ url, invoiceKey, amount, expiry }, { me }) {
> const memo = me.hideInvoiceDesc ? undefined : 'autowithdraw to LNbits from SN'
> const body = JSON.stringify({ amount, unit: 'sat', expiry, memo, out: false })
>
> + url = 'http://lnbits:5000'
> const res = await fetch(url + path, { method: 'POST', headers, body })
> if (!res.ok) {
> const errBody = await res.json()
> ```
>
To get access to the superuser, you need to visit the admin UI:
http://localhost:5001/wallet?usr=e46288268b67457399a5fca81809573e
After that, the cookies will be set to access this wallet:
http://localhost:5001/wallet?&wal=15ffe06c74cc4082a91f528d016d9028
Or simply copy the keys from here:
* admin key: `640cc7b031eb427c891eeaa4d9c34180`
* invoice key: `5deed7cd634e4306bb5e696f4a03cdac`
( These keys can be found under `Node URL, API keys and API docs`. )
To use the same URL to connect to LNbits in the browser and server during local development, `localhost:<port>` is mapped to `lnbits:5000` on the server.

View File

@ -28,9 +28,14 @@ export async function createInvoice (
out: false
})
const hostname = url.replace(/^https?:\/\//, '')
let hostname = url.replace(/^https?:\/\//, '')
const agent = getAgent({ hostname })
if (process.env.NODE_ENV !== 'production' && hostname.startsWith('localhost:')) {
// to make it possible to attach LNbits for receives during local dev
hostname = 'lnbits:5000'
}
const res = await fetch(`${agent.protocol}//${hostname}${path}`, {
method: 'POST',
headers,

View File

@ -36,7 +36,7 @@ export default async function wrapInvoice (bolt11, { msats, description, descrip
throw new Error('Unable to decode invoice')
}
console.log('invoice', inv.mtokens, inv.expires_at, inv.cltv_delta)
console.log('invoice', inv.id, inv.mtokens, inv.expires_at, inv.cltv_delta, inv.destination)
// validate outgoing amount
if (inv.mtokens) {
@ -77,6 +77,8 @@ export default async function wrapInvoice (bolt11, { msats, description, descrip
case 49:
case 149: // trampoline routing
case 151: // electrum trampoline routing
case 262:
case 263: // blinded paths
break
default:
throw new Error(`Unsupported feature bit: ${f.bit}`)

View File

@ -4,7 +4,10 @@ import { createInvoice } from 'wallets/server'
export async function autoWithdraw ({ data: { id }, models, lnd }) {
const user = await models.user.findUnique({ where: { id } })
if (user.autoWithdrawThreshold === null || user.autoWithdrawMaxFeePercent === null) return
if (
user.autoWithdrawThreshold === null ||
user.autoWithdrawMaxFeePercent === null ||
user.autoWithdrawMaxFeeTotal === null) return
const threshold = satsToMsats(user.autoWithdrawThreshold)
const excess = Number(user.msats - threshold)
@ -13,7 +16,10 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
if (excess < Number(threshold) * 0.1) return
// floor fee to nearest sat but still denominated in msats
const maxFeeMsats = msatsSatsFloor(Math.ceil(excess * (user.autoWithdrawMaxFeePercent / 100.0)))
const maxFeeMsats = msatsSatsFloor(Math.max(
Math.ceil(excess * (user.autoWithdrawMaxFeePercent / 100.0)),
Number(satsToMsats(user.autoWithdrawMaxFeeTotal))
))
// msats will be floored by createInvoice if it needs to be
const msats = BigInt(excess) - maxFeeMsats

View File

@ -346,12 +346,12 @@ export async function autoDropBolt11s ({ models, lnd }) {
SELECT id, hash, bolt11
FROM "Withdrawl"
WHERE "userId" IN (SELECT id FROM users WHERE "autoDropBolt11s")
AND now() > created_at + interval '${retention}'
AND now() > created_at + ${retention}::INTERVAL
AND hash IS NOT NULL
AND status IS NOT NULL
), updated_rows AS (
UPDATE "Withdrawl"
SET hash = NULL, bolt11 = NULL
SET hash = NULL, bolt11 = NULL, preimage = NULL
FROM to_be_updated
WHERE "Withdrawl".id = to_be_updated.id)
SELECT * FROM to_be_updated;`
@ -364,7 +364,7 @@ export async function autoDropBolt11s ({ models, lnd }) {
console.error(`Error removing invoice with hash ${invoice.hash}:`, error)
await models.withdrawl.update({
where: { id: invoice.id },
data: { hash: invoice.hash, bolt11: invoice.bolt11 }
data: { hash: invoice.hash, bolt11: invoice.bolt11, preimage: invoice.preimage }
})
}
}