feat(machine): persist exchange rate and currency in transactions
Add exchange_rate (sats per fiat unit) and currency columns to the transactions table so transaction economics can be audited after the fact. Includes schema migration v2 (fee columns) and v3 (rate/currency), updated IPC types, and atm-transactions CLI output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d64f6cd9ab
commit
310d0edb99
5 changed files with 65 additions and 4 deletions
|
|
@ -15,7 +15,7 @@ import fs from 'node:fs'
|
|||
|
||||
let db: Database.Database | null = null
|
||||
|
||||
const SCHEMA_VERSION = '1'
|
||||
const SCHEMA_VERSION = '3'
|
||||
|
||||
function getDbPath(): string {
|
||||
const prodDir = '/var/lib/lamassu-atm'
|
||||
|
|
@ -68,6 +68,10 @@ export function initDatabase(dbPath?: string): void {
|
|||
type TEXT NOT NULL CHECK (type IN ('cash_in', 'cash_out')),
|
||||
fiat_cents INTEGER NOT NULL,
|
||||
sats INTEGER NOT NULL,
|
||||
fee_sats INTEGER NOT NULL DEFAULT 0,
|
||||
fee_percent REAL NOT NULL DEFAULT 0,
|
||||
exchange_rate REAL NOT NULL DEFAULT 0,
|
||||
currency TEXT NOT NULL DEFAULT 'GTQ',
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
|
|
@ -79,12 +83,31 @@ export function initDatabase(dbPath?: string): void {
|
|||
);
|
||||
`)
|
||||
|
||||
// Seed meta + cashbox if first run
|
||||
// Seed meta + cashbox if first run, or run migrations
|
||||
const existing = db.prepare('SELECT value FROM meta WHERE key = ?').get('schema_version') as
|
||||
| { value: string }
|
||||
| undefined
|
||||
if (!existing) {
|
||||
db.prepare('INSERT INTO meta (key, value) VALUES (?, ?)').run('schema_version', SCHEMA_VERSION)
|
||||
} else if (existing.value === '1') {
|
||||
// Migration v1 → v2: add fee columns to transactions
|
||||
db.exec(`
|
||||
ALTER TABLE transactions ADD COLUMN fee_sats INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE transactions ADD COLUMN fee_percent REAL NOT NULL DEFAULT 0;
|
||||
`)
|
||||
db.prepare('UPDATE meta SET value = ? WHERE key = ?').run('2', 'schema_version')
|
||||
console.log('[StateStore] Migrated schema v1 → v2 (added fee columns)')
|
||||
existing.value = '2'
|
||||
}
|
||||
|
||||
if (existing && existing.value === '2') {
|
||||
// Migration v2 → v3: add exchange rate and currency
|
||||
db.exec(`
|
||||
ALTER TABLE transactions ADD COLUMN exchange_rate REAL NOT NULL DEFAULT 0;
|
||||
ALTER TABLE transactions ADD COLUMN currency TEXT NOT NULL DEFAULT 'GTQ';
|
||||
`)
|
||||
db.prepare('UPDATE meta SET value = ? WHERE key = ?').run('3', 'schema_version')
|
||||
console.log('[StateStore] Migrated schema v2 → v3 (added exchange_rate, currency)')
|
||||
}
|
||||
|
||||
const cashboxRow = db.prepare('SELECT id FROM cashbox WHERE id = 1').get()
|
||||
|
|
@ -214,6 +237,10 @@ interface TransactionInput {
|
|||
type: 'cash_in' | 'cash_out'
|
||||
fiatCents: number
|
||||
sats: number
|
||||
feeSats: number
|
||||
feePercent: number
|
||||
exchangeRate: number
|
||||
currency: string
|
||||
bills: { denomination: number; count: number }[]
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +251,7 @@ export function recordTransaction(tx: TransactionInput): void {
|
|||
if (!db) throw new Error('Database not initialized')
|
||||
|
||||
const insertTx = db.prepare(
|
||||
'INSERT INTO transactions (txid, type, fiat_cents, sats, created_at) VALUES (?, ?, ?, ?, ?)'
|
||||
'INSERT INTO transactions (txid, type, fiat_cents, sats, fee_sats, fee_percent, exchange_rate, currency, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
)
|
||||
const insertBill = db.prepare(
|
||||
'INSERT INTO transaction_bills (txid, denomination, count) VALUES (?, ?, ?)'
|
||||
|
|
@ -237,7 +264,17 @@ export function recordTransaction(tx: TransactionInput): void {
|
|||
)
|
||||
|
||||
const run = db.transaction((t: TransactionInput) => {
|
||||
insertTx.run(t.txid, t.type, t.fiatCents, t.sats, Date.now())
|
||||
insertTx.run(
|
||||
t.txid,
|
||||
t.type,
|
||||
t.fiatCents,
|
||||
t.sats,
|
||||
t.feeSats,
|
||||
t.feePercent,
|
||||
t.exchangeRate,
|
||||
t.currency,
|
||||
Date.now()
|
||||
)
|
||||
|
||||
for (const bill of t.bills) {
|
||||
insertBill.run(t.txid, bill.denomination, bill.count)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue