Files

111 lines
5.9 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:1aab7b8b0f1a8c9ae297214b001ed051798627cb16552a7cd6582bdf219ea871
import * as db from '../db.js';
import { ENUMS } from '../../shared/config.js';
import { assertEnum, nowIso } from './_shared.js';
import { fail } from '../../shared/app-error.js';
import { trackDelete, trackUpdate } from './change-log.js';
import { hasFeature } from '../license.mjs';
import { services } from '../registers.mjs';
const nwc = () => services.get('nwc');
export async function purgeWalletSourceSecret(id) {
await nwc()?.deleteNwcUri(id);
}
export function assertSourceMatchesAccount(tabel, accountId, sourceId) {
if (sourceId == null) return;
const bron = db.one(`SELECT accountId FROM WalletSource WHERE id = ?`, [sourceId]);
if (!bron) throw new Error(`${tabel}: source ${sourceId} does not exist.`);
if (Number(bron.accountId) !== Number(accountId)) {
throw new Error(`${tabel}: source ${sourceId} belongs to account ${bron.accountId}, not to ${accountId}.`);
}
}
export function listWalletSources(accountId) {
return db.all(`SELECT s.*, c.name AS connection_name, c.url AS connection_url
FROM WalletSource s
LEFT JOIN Connection c ON c.id = s.connectionId
WHERE s.accountId = ? ORDER BY s.id`, [accountId]);
}
export function listAllWalletSources() {
return db.all(`SELECT s.*, a.name AS account_name FROM WalletSource s
JOIN Account a ON a.id = s.accountId ORDER BY a.name, s.id`);
}
function assertLndConnection(connectionId) {
const c = db.one(`SELECT id, kind FROM Connection WHERE id = ?`, [connectionId]);
if (!c) fail('accounts.lndNoConnection', null, 'Pick an LND connection.');
if (c.kind !== 'lnd') fail('accounts.sourceNotLnd', null, 'The chosen connection is not an LND connection.');
}
function connectionFor(kind, connectionId) {
if (kind === 'lnd') { assertLndConnection(Number(connectionId)); return Number(connectionId); }
if (ENUMS.ONCHAIN_SOURCE_KIND.includes(kind) && connectionId != null && connectionId !== '') {
assertLndConnection(Number(connectionId));
return Number(connectionId);
}
return null;
}
const LIGHTNING_SOURCE = new Set(['nwc', 'lnd']);
function assertLightningAllowed(kind) {
if (LIGHTNING_SOURCE.has(kind) && (!hasFeature('lightning') || (kind === 'nwc' && !nwc()))) {
fail('connections.lightningPaid', null, 'Lightning connections belong to the paid version.');
}
}
export async function createWalletSource({ accountId, kind, value, name, scriptType, connectionId = null, secret = null }) {
assertEnum('kind', ENUMS.WALLET_SOURCE_KIND, kind);
assertLightningAllowed(kind);
if (kind === 'xpub') assertEnum('scriptType', ENUMS.SCRIPT_TYPE, scriptType);
if (kind === 'nwc' && !secret) fail('accounts.sourceUriMissing', null, 'The NWC URI is missing.');
const verbinding = connectionFor(kind, connectionId);
db.run(`INSERT INTO WalletSource (accountId, kind, value, name, scriptType, connectionId, createdAt) VALUES (?,?,?,?,?,?,?)`,
[accountId, kind, kind === 'lnd' ? '' : value, name ?? null, kind === 'xpub' ? scriptType : null,
verbinding, nowIso()]);
const id = db.lastId();
if (kind === 'nwc') await nwc().setNwcUri(id, secret);
await db.persist(); return id;
}
export async function updateWalletSource(id, { kind, value, name, scriptType, connectionId = null, secret = null }) {
assertEnum('kind', ENUMS.WALLET_SOURCE_KIND, kind);
if (kind === 'xpub') assertEnum('scriptType', ENUMS.SCRIPT_TYPE, scriptType);
const oud = db.one(`SELECT kind FROM WalletSource WHERE id = ?`, [id]);
if (oud?.kind !== kind) assertLightningAllowed(kind);
const nieuwValue = kind === 'lnd' ? '' : value;
const nieuwConnectie = connectionFor(kind, connectionId);
trackUpdate('WalletSource', id, () =>
db.run(`UPDATE WalletSource SET kind=?, value=?, name=?, scriptType=?, connectionId=?,
balanceSats = CASE WHEN kind = ? AND value = ? AND connectionId IS ? THEN balanceSats END,
lastScannedAt = CASE WHEN kind = ? AND value = ? AND connectionId IS ? THEN lastScannedAt END
WHERE id=?`,
[kind, nieuwValue, name ?? null, kind === 'xpub' ? scriptType : null, nieuwConnectie,
kind, nieuwValue, nieuwConnectie, kind, nieuwValue, nieuwConnectie, id]));
if (kind === 'nwc' && secret) await nwc().setNwcUri(id, secret);
if (kind !== 'nwc') await nwc()?.deleteNwcUri(id);
await db.persist();
}
export async function recordSourceBalance(id, sats) {
const bron = db.one(`SELECT kind FROM WalletSource WHERE id = ?`, [Number(id)]);
if (!bron) throw new Error(`Wallet source ${id} does not exist.`);
if (bron.kind !== 'lnd' && bron.kind !== 'nwc') throw new Error(`Wallet source ${id} is on-chain; its balance comes from its addresses.`);
if (!Number.isInteger(sats) || sats < 0) throw new Error(`A balance is a whole number of sats from zero: '${sats}'.`);
db.run(`UPDATE WalletSource SET balanceSats = ?, lastScannedAt = ? WHERE id = ?`, [sats, nowIso(), Number(id)]);
await db.persist();
}
export function lightningBalances() {
return db.all(`SELECT WalletSource.accountId, Account.name AS account_name,
SUM(WalletSource.balanceSats) AS sats, MAX(WalletSource.lastScannedAt) AS scannedAt
FROM WalletSource
JOIN Account ON Account.id = WalletSource.accountId
WHERE WalletSource.lastScannedAt IS NOT NULL
AND (WalletSource.kind = 'lnd'
OR (WalletSource.kind = 'nwc' AND NOT EXISTS (
SELECT 1 FROM WalletSource node
WHERE node.accountId = WalletSource.accountId AND node.kind = 'lnd')))
GROUP BY WalletSource.accountId, Account.name
ORDER BY Account.name`);
}
export async function deleteWalletSource(id) {
await purgeWalletSourceSecret(id);
trackDelete('WalletSource', id, () => db.run(`DELETE FROM WalletSource WHERE id=?`, [id]));
await db.persist();
}