// Generated, do not edit. sha256:a4f989c4fcb487fdb8c1ab80d536f8fa5a1a10a2f0357c22596ab060e7b38694 import * as db from '../db.js'; import { ENUMS } from '../../shared/config.js'; import { assertEnum, nowIso } from './_shared.js'; export async function syncAddressLinks() { const method = 'exact'; assertEnum('method', ENUMS.LINK_METHOD, method); db.run(`INSERT OR IGNORE INTO WalletTransactionEventLink (walletTransactionId, eventId, method, createdAt) SELECT w.id, e.id, ?1, ?2 FROM WalletTransaction w JOIN Event e ON e.externalAddress = w.address WHERE w.direction = 'in' AND w.address IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ReconcileProposal p WHERE p.walletTransactionId = w.id AND p.resolvedEventId = e.id AND p.status = 'ignored')`, [method, nowIso()]); let n = db.one(`SELECT changes() AS n`).n; db.run(`INSERT OR IGNORE INTO WalletTransactionEventLink (walletTransactionId, eventId, method, createdAt) SELECT w.id, e.id, ?1, ?2 FROM WalletTransaction w JOIN Event e ON e.source = 'lightning' AND (e.fromAccountId = w.accountId OR e.toAccountId = w.accountId) AND (e.importReference = 'ln:' || w.txid OR (w.txid LIKE 'fwd:%' AND e.importReference = 'lnfwd:' || w.blockTime)) WHERE w.layer = 'lightning' AND NOT EXISTS (SELECT 1 FROM ReconcileProposal p WHERE p.walletTransactionId = w.id AND p.resolvedEventId = e.id AND p.status = 'ignored')`, [method, nowIso()]); n += db.one(`SELECT changes() AS n`).n; db.run(`INSERT OR IGNORE INTO WalletTransactionEventLink (walletTransactionId, eventId, method, createdAt) SELECT w.id, l.eventId, ?1, ?2 FROM WalletTransaction w JOIN WalletTransaction twin ON twin.accountId = w.accountId AND twin.txid = w.txid AND twin.layer = 'lightning' AND twin.id <> w.id JOIN WalletTransactionEventLink l ON l.walletTransactionId = twin.id WHERE w.layer = 'lightning' AND NOT EXISTS (SELECT 1 FROM ReconcileProposal p WHERE p.walletTransactionId = w.id AND p.resolvedEventId = l.eventId AND p.status = 'ignored')`, [method, nowIso()]); n += db.one(`SELECT changes() AS n`).n; const asset = onchainAsset(); if (asset) { db.run(`INSERT OR IGNORE INTO ReconcileProposal (accountId, assetId, walletTransactionId, transactionHash, amount, direction, suggestedType, status, resolvedEventId, detectedAt) SELECT w.accountId, ?1, w.id, w.txid, w.valueSats, w.direction, CASE WHEN e.type = 'transfer' THEN 'transfer' ELSE 'trade' END, 'auto_linked', e.id, ?2 FROM WalletTransactionEventLink l JOIN WalletTransaction w ON w.id = l.walletTransactionId JOIN Event e ON e.id = l.eventId WHERE l.method = 'exact' AND NOT EXISTS (SELECT 1 FROM ReconcileProposal p WHERE p.walletTransactionId = w.id AND p.resolvedEventId = e.id)`, [asset.id, nowIso()]); } const changed = n + db.one(`SELECT changes() AS n`).n; if (changed > 0) await db.persist(); return n; } export function eventLinkMap() { const m = new Map(); for (const r of db.all(`SELECT eventId, method FROM WalletTransactionEventLink`)) m.set(r.eventId, r.method); return m; } export function walletTxEventMap() { const m = new Map(); const rows = db.all(`SELECT l.walletTransactionId, l.method, e.id AS eventId, e.type, e.amount, e.unitFee, e.fromAccountId, e.toAccountId, fa.name AS from_name, ta.name AS to_name FROM WalletTransactionEventLink l JOIN Event e ON e.id = l.eventId LEFT JOIN Account fa ON fa.id = e.fromAccountId LEFT JOIN Account ta ON ta.id = e.toAccountId`); for (const r of rows) { if (!m.has(r.walletTransactionId)) m.set(r.walletTransactionId, []); m.get(r.walletTransactionId).push(r); } return m; } export async function linkTxEvent(walletTxId, eventId, method) { assertEnum('method', ENUMS.LINK_METHOD, method); db.run(`INSERT OR IGNORE INTO WalletTransactionEventLink (walletTransactionId, eventId, method, createdAt) VALUES (?,?,?,?)`, [walletTxId, eventId, method, nowIso()]); await db.persist(); } export function linkProposalTxSoft(walletTxId, eventId) { const method = 'soft'; assertEnum('method', ENUMS.LINK_METHOD, method); db.run(`INSERT OR IGNORE INTO WalletTransactionEventLink (walletTransactionId, eventId, method, createdAt) VALUES (?,?,?,?)`, [walletTxId, eventId, method, nowIso()]); } export async function unlinkTxEvent(walletTxId, eventId) { db.run(`DELETE FROM WalletTransactionEventLink WHERE walletTransactionId = ? AND eventId = ?`, [walletTxId, eventId]); db.run(`UPDATE ReconcileProposal SET status = 'ignored' WHERE walletTransactionId = ? AND resolvedEventId = ? AND status = 'auto_linked'`, [walletTxId, eventId]); await db.persist(); } export function onchainAsset() { return db.one(`SELECT * FROM Asset WHERE symbol = 'BTC'`) || db.one(`SELECT * FROM Asset WHERE kind = 'crypto' ORDER BY symbol LIMIT 1`) || null; } export function linksForTx(walletTxId) { return db.all(` SELECT tel.eventId AS id, tel.method, e.type, e.occurredAt, e.amount, a.symbol AS asset_symbol, lf.name AS from_name, lt.name AS to_name FROM WalletTransactionEventLink tel JOIN Event e ON e.id = tel.eventId JOIN Asset a ON a.id = e.assetId LEFT JOIN Account lf ON lf.id = e.fromAccountId LEFT JOIN Account lt ON lt.id = e.toAccountId WHERE tel.walletTransactionId = ? ORDER BY e.occurredAt DESC, e.id DESC`, [walletTxId]); } export function linksForEvent(eventId) { return db.all(` SELECT tel.walletTransactionId AS id, tel.method, t.direction, t.valueSats, t.blockTime, t.txid, t.address, ac.name AS account_name FROM WalletTransactionEventLink tel JOIN WalletTransaction t ON t.id = tel.walletTransactionId LEFT JOIN Account ac ON ac.id = t.accountId WHERE tel.eventId = ? ORDER BY (t.blockTime IS NULL) DESC, t.blockTime DESC, t.id DESC`, [eventId]); } export function eventCandidatesForTx(walletTxId, assetId) { return db.all(` SELECT e.id, e.type, e.occurredAt, e.amount, a.symbol AS asset_symbol, lf.name AS from_name, lt.name AS to_name FROM Event e JOIN Asset a ON a.id = e.assetId LEFT JOIN Account lf ON lf.id = e.fromAccountId LEFT JOIN Account lt ON lt.id = e.toAccountId WHERE e.assetId = ?2 AND e.type <> 'reservation' AND e.id NOT IN (SELECT eventId FROM WalletTransactionEventLink WHERE walletTransactionId = ?1) ORDER BY ABS(strftime('%s', e.occurredAt) - strftime('%s', COALESCE((SELECT blockTime FROM WalletTransaction WHERE id = ?1), (SELECT createdAt FROM WalletTransaction WHERE id = ?1)))) ASC, e.id DESC LIMIT 100`, [walletTxId, assetId]); } export function txCandidatesForEvent(eventId) { return db.all(` SELECT t.id, t.direction, t.valueSats, t.blockTime, t.txid, t.address, ac.name AS account_name, l.name AS label_name FROM WalletTransaction t LEFT JOIN Account ac ON ac.id = t.accountId LEFT JOIN Label l ON l.id = t.labelId WHERE t.id NOT IN (SELECT walletTransactionId FROM WalletTransactionEventLink WHERE eventId = ?1) ORDER BY ABS(strftime('%s', COALESCE(t.blockTime, t.createdAt)) - strftime('%s', (SELECT occurredAt FROM Event WHERE id = ?1))) ASC, t.id DESC LIMIT 100`, [eventId]); }