// Copyright (C) 2024 Riften Labs AS // // This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later. // A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use anyhow::{Context, Result}; use rusqlite::{params, Connection}; pub mod apy; pub mod bcmr; pub mod contract; pub mod err; pub mod pool; pub mod price; pub mod tokens; pub mod tvl; pub mod tx; pub mod user; pub struct ResponseCacheInner { update_timestamp: i64, value: Option, in_progress: bool, } pub type ResponseCache = Arc>>; fn all_time_volume(db: &Connection, end_timestamp: u64) -> Result> { let sql = " SELECT uf1.token_id, SUM(ABS(uf1.sats - COALESCE(uf2.sats, 0))) AS total_volume_sats FROM utxo_funding uf1 INNER JOIN utxo_funding uf2 ON uf1.spent_utxo_hash = uf2.new_utxo_hash JOIN tx ON uf1.txid = tx.txid WHERE COALESCE(tx.first_seen_timestamp, tx.mtp_timestamp) <= ? GROUP BY uf1.token_id"; let mut stmt = db.prepare(sql)?; let volume_iter = stmt.query_map(params![end_timestamp], |row| { Ok((row.get::<_, String>(0)?, row.get::<_, i64>(1)?)) })?; let mut volumes = Vec::new(); for volume in volume_iter { volumes.push(volume?); } Ok(volumes) } fn period_volume( db: &Connection, begin_timestamp: u64, end_timestamp: u64, ) -> Result> { let sql = " SELECT uf1.token_id, SUM(ABS(uf1.sats - COALESCE(uf2.sats, 0))) AS total_volume_sats FROM utxo_funding uf1 INNER JOIN utxo_funding uf2 ON uf1.spent_utxo_hash = uf2.new_utxo_hash JOIN tx ON uf1.txid = tx.txid WHERE COALESCE(tx.first_seen_timestamp, tx.mtp_timestamp) BETWEEN ? AND ? GROUP BY uf1.token_id "; let mut stmt = db.prepare(sql)?; let volume_iter = stmt.query_map(params![begin_timestamp, end_timestamp], |row| { Ok((row.get::<_, String>(0)?, row.get::<_, i64>(1)?)) })?; let mut volumes = Vec::new(); for volume in volume_iter { volumes.push(volume?); } Ok(volumes) } pub fn contract_volume( db: &Connection, end_timestamp: u64, ) -> Result> { let one_day_seconds = 86400_u64; let thirty_days_seconds = 30 * 86400_u64; let one_day_begin_timestamp = end_timestamp .checked_sub(one_day_seconds) .context("timestamp underflow")?; let thirty_days_begin_timestamp = end_timestamp .checked_sub(thirty_days_seconds) .context("timestamp underflow")?; let all_time = all_time_volume(db, end_timestamp)?; let one_day = period_volume(db, one_day_begin_timestamp, end_timestamp)?; let thirty_days = period_volume(db, thirty_days_begin_timestamp, end_timestamp)?; let mut result = HashMap::new(); for (token_id, volume) in all_time.into_iter() { result.insert(token_id, (volume, 0, 0)); } for (token_id, day_volume) in one_day.into_iter() { if let Some((_, _, one_day_volume)) = result.get_mut(&token_id) { *one_day_volume = day_volume; } else { result.insert(token_id.clone(), (0, 0, day_volume)); } } for (token_id, month_volume) in thirty_days.into_iter() { if let Some((_, thirty_day_volume, _)) = result.get_mut(&token_id) { *thirty_day_volume = month_volume; } else { result.insert(token_id.clone(), (0, month_volume, 0)); } } Ok(result) }