use anyhow::Result; use bitcoin_hashes::hex::ToHex; use riftenlabs_defi::cauldron::ParsedContract; use rusqlite::{params, Connection}; pub fn prepare_tables(conn: &Connection) { conn.execute( "CREATE TABLE utxo_funding ( new_utxo_hash TEXT PRIMARY KEY, timestamp BIGINT, new_utxo_txid TEXT, new_utxo_n INT, sats BIGINT, token_amount BIGINT, token_id TEXT );", [], ) .unwrap(); conn.execute( "CREATE INDEX idx_funding_timestamp ON utxo_funding (timestamp);", [], ) .unwrap(); conn.execute( "CREATE TABLE utxo_spending ( spent_utxo_hash TEXT PRIMARY KEY, timestamp BIGINT );", [], ) .unwrap(); conn.execute( "CREATE INDEX idx_spending_timestamp ON utxo_spending (timestamp);", [], ) .unwrap(); conn.execute( "CREATE TABLE cfg_number ( key TEXT PRIMARY KEY, value INT )", [], ) .unwrap(); } pub fn config_set(conn: &Connection, key: &str, value: i64) { let mut stmt = conn .prepare("INSERT OR REPLACE INTO cfg_number (key, value) VALUES (?, ?)") .unwrap(); stmt.execute(params![key, value]).unwrap(); } pub fn config_get(conn: &Connection, key: &str) -> Result> { let mut stmt = conn.prepare("SELECT value FROM cfg_number WHERE key = ?")?; let mut row = stmt.query([key])?; Ok(row.next()?.map(|r| r.get(0).unwrap())) } pub fn insert_utxo_funding( con: &Connection, timestamp: u32, cauldrons: &Vec, ) -> Result<()> { let mut statement = con .prepare("INSERT OR IGNORE INTO utxo_funding (new_utxo_hash, timestamp, new_utxo_txid, new_utxo_n, sats, token_amount, token_id) VALUES (?, ?, ?, ?, ?, ?, ?)")?; for c in cauldrons { if c.new_utxo_hash.is_none() { continue; } statement.execute(params![ c.new_utxo_hash.unwrap().to_hex(), timestamp, c.new_utxo_txid.unwrap().to_hex(), c.new_utxo_n.unwrap(), c.sats.unwrap(), c.token_amount.unwrap(), c.token_id.unwrap().to_hex(), ])?; } Ok(()) } pub fn insert_utxo_spending( conn: &Connection, timestamp: u32, cauldrons: &Vec, ) -> Result<()> { let mut statement = conn.prepare( "INSERT OR IGNORE INTO utxo_spending (spent_utxo_hash, timestamp) VALUES (?, ?)", )?; for c in cauldrons { statement.execute(params![c.spent_utxo_hash.to_hex(), timestamp])?; } Ok(()) } pub fn get_token_tvl( connection: &Connection, max_timestamp: usize, ) -> Result> { let mut statement = connection.prepare( " SELECT uf.token_id, SUM(uf.token_amount) AS total_unspent_token_amount, SUM(uf.sats) AS total_unspent_sats FROM utxo_funding uf LEFT JOIN utxo_spending us ON uf.new_utxo_hash = us.spent_utxo_hash WHERE (us.timestamp IS NULL OR us.timestamp > ?) AND uf.timestamp <= ? GROUP BY uf.token_id", )?; let tvl: Vec<(String, u64, u64)> = statement .query_and_then([max_timestamp, max_timestamp], |row| { let token_id: String = row.get(0)?; let token_amount: u64 = row.get(1)?; let sats: u64 = row.get(2)?; Ok((token_id, token_amount, sats)) }) .unwrap() .map(|row: Result<(String, u64, u64)>| row.unwrap()) .collect(); Ok(tvl) }