132 lines
3.5 KiB
Rust
132 lines
3.5 KiB
Rust
// Copyright (C) 2024-2026 Whiterun LLC
|
|
//
|
|
// 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 candlesticks;
|
|
pub mod contract;
|
|
pub mod err;
|
|
pub mod oracle;
|
|
pub mod pool;
|
|
pub mod price;
|
|
pub mod response;
|
|
pub mod tokens;
|
|
pub mod tvl;
|
|
pub mod tx;
|
|
pub mod user;
|
|
pub mod volume;
|
|
|
|
pub struct ResponseCacheInner {
|
|
update_timestamp: i64,
|
|
value: Option<serde_json::Value>,
|
|
in_progress: bool,
|
|
}
|
|
|
|
pub type ResponseCache = Arc<Mutex<HashMap<String, ResponseCacheInner>>>;
|
|
|
|
fn all_time_volume(db: &Connection, end_timestamp: u64) -> Result<Vec<(String, i64)>> {
|
|
let sql = "
|
|
SELECT
|
|
p.token_id,
|
|
SUM(ABS(phe.sats_delta)) AS total_volume_sats
|
|
FROM pool_history_entry phe
|
|
JOIN pool p ON phe.pool = p.creation_utxo
|
|
JOIN tx ON phe.txid = tx.txid
|
|
WHERE tx.effective_timestamp <= ?
|
|
GROUP BY p.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<Vec<(String, i64)>> {
|
|
let sql = "
|
|
SELECT
|
|
p.token_id,
|
|
SUM(ABS(phe.sats_delta)) AS total_volume_sats
|
|
FROM pool_history_entry phe
|
|
JOIN pool p ON phe.pool = p.creation_utxo
|
|
JOIN tx ON phe.txid = tx.txid
|
|
WHERE tx.effective_timestamp BETWEEN ? AND ?
|
|
GROUP BY p.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<HashMap<String, (i64, i64, i64)>> {
|
|
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)
|
|
}
|