112 lines
5.9 KiB
JavaScript
112 lines
5.9 KiB
JavaScript
// 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();
|
|
}
|