// Generated, do not edit. sha256:c2c77c87420dd87960f52c5550a95a9491166b15f248300e49e13aae650240fc import { nowUtcIso } from '../../shared/time.js'; import { fail } from '../../shared/app-error.js'; import { assetPositions, eventAccountIdsByAsset, eventAssetIdsByAccount, eventsCountByAccount, eventsNetByAccount, firstTaxEventDate } from './events.js'; import { anchorAt, anchorsBetween, createValuation, firstAnchorDate, latestAnchors, listValuations, updateValuation, valuationDiff, valuationGap, valuationReconcilable } from './valuations.js'; import { accountOnchainBalance } from './wallet-addresses.js'; import { lightningBalances } from './wallet-sources.js'; import { onchainAsset } from './tx-links.js'; export function positionAt(accountId, assetId, atIso) { const anker = anchorAt(accountId, assetId, atIso); if (!anker) return eventsNetByAccount(accountId, assetId, atIso); const naAnker = eventsNetByAccount(accountId, assetId, atIso) - eventsNetByAccount(accountId, assetId, anker.rateAt); return anker.balance + naAnker; } export function positionsByAsset(atIso, { ownOnly = false } = {}) { const peil = atIso ?? nowUtcIso(); const correctie = new Map(); for (const anker of latestAnchors(peil, { ownOnly })) { const diff = valuationDiff(anker); if (diff) correctie.set(anker.assetId, (correctie.get(anker.assetId) || 0) + diff); } const rijen = assetPositions(peil, { ownOnly }); if (correctie.size === 0) return rijen; return rijen.map(r => (correctie.has(r.id) ? { ...r, net: r.net + correctie.get(r.id) } : r)); } export function unexplainedChange(accountId, assetId, fromIso, untilIso) { const stand = positionAt(accountId, assetId, untilIso) - positionAt(accountId, assetId, fromIso); const geboekt = eventsNetByAccount(accountId, assetId, untilIso) - eventsNetByAccount(accountId, assetId, fromIso); return stand - geboekt; } export function firstPositionDate() { const gebeurtenis = firstTaxEventDate(); const anker = firstAnchorDate(); if (!gebeurtenis) return anker; if (!anker) return gebeurtenis; return gebeurtenis < anker ? gebeurtenis : anker; } export function unexplainedByPair(fromIso, untilIso) { const uit = []; for (const anker of latestAnchors(untilIso)) { const units = unexplainedChange(anker.accountId, anker.assetId, fromIso, untilIso); if (!units) continue; let openingUnits = 0; for (const v of anchorsBetween(anker.accountId, anker.assetId, fromIso, untilIso)) { if (valuationReconcilable(v) && !v.gapAcceptedAt) continue; openingUnits += valuationGap(v) || 0; } uit.push({ accountId: anker.accountId, assetId: anker.assetId, units, openingUnits, hasLedger: eventsCountByAccount(anker.accountId, anker.assetId, untilIso) > 0, }); } return uit; } export function assetPositionsByAccount(assetId, atIso) { const peil = atIso ?? nowUtcIso(); const accounts = new Set(eventAccountIdsByAsset(assetId)); for (const anker of latestAnchors(peil)) if (anker.assetId === assetId) accounts.add(anker.accountId); const uit = []; for (const accountId of accounts) { const saldo = positionAt(accountId, assetId, peil); if (saldo !== 0) uit.push({ accountId, saldo }); } uit.sort((a, b) => b.saldo - a.saldo || a.accountId - b.accountId); const totaal = positionsByAsset(peil).find(r => r.id === assetId); const rest = (totaal ? totaal.net : 0) - uit.reduce((s, r) => s + r.saldo, 0); if (rest !== 0) uit.push({ accountId: null, saldo: rest }); return uit; } export function accountPositions(accountId, atIso) { const peil = atIso ?? nowUtcIso(); const assets = new Set(eventAssetIdsByAccount(accountId)); for (const anker of latestAnchors(peil)) if (anker.accountId === accountId) assets.add(anker.assetId); const uit = []; for (const assetId of [...assets].sort((a, b) => a - b)) { const saldo = positionAt(accountId, assetId, peil); if (saldo !== 0) uit.push({ assetId, saldo }); } return uit; } export function accountHoldings(accountIds = [], atIso) { const peil = atIso ?? nowUtcIso(); const uit = []; for (const accountId of accountIds) { for (const { assetId, saldo } of accountPositions(accountId, peil)) { const anker = anchorAt(accountId, assetId, peil); uit.push({ accountId, assetId, saldo, anchor: anker ? { valuedOn: anker.valuedOn, rateAt: anker.rateAt, balance: anker.balance } : null }); } } return uit; } function measuredNodeBalance(row) { return accountOnchainBalance(row.accountId) + (Number(row.sats) || 0); } export function lightningBalanceDiffs() { const asset = onchainAsset(); if (!asset) return []; const uit = []; for (const row of lightningBalances()) { const measured = measuredNodeBalance(row); const ledger = positionAt(row.accountId, asset.id, row.scannedAt); if (measured !== ledger) { uit.push({ accountId: row.accountId, account_name: row.account_name, assetId: asset.id, measured, ledger, gap: measured - ledger, scannedAt: row.scannedAt }); } } return uit; } export async function recordLightningBalance(accountId, { valuedOn, fiatRate } = {}) { const row = lightningBalances().find(r => r.accountId === Number(accountId)); const asset = onchainAsset(); if (!row || !asset) throw new Error(`Account ${accountId} has no updated lightning source.`); if (!/^\d{4}-\d{2}-\d{2}$/.test(String(valuedOn))) throw new Error(`A balance needs its day: '${valuedOn}'.`); if (!(Number(fiatRate) > 0)) throw new Error(`A balance needs a rate above zero: '${fiatRate}'.`); const v = { assetId: asset.id, accountId: row.accountId, valuedOn, balance: measuredNodeBalance(row), fiatRate: Number(fiatRate), rateAt: row.scannedAt, source: 'onchain' }; const zelfdeDag = listValuations().find(x => x.accountId === v.accountId && x.assetId === v.assetId && x.valuedOn === valuedOn); if (zelfdeDag && zelfdeDag.source !== 'onchain') { fail('valuations.lightningSameDay', { date: valuedOn }, `There is already a balance on ${valuedOn} for this account; it is not overwritten.`); } if (zelfdeDag) { await updateValuation(zelfdeDag.id, v); return zelfdeDag.id; } return createValuation(v); }