2024-04-03 09:49:44 +02:00
|
|
|
// 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 anyhow::{Context, Result};
|
|
|
|
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
|
|
|
|
use rocket::{get, http::Status, response::status::Custom, serde::json::Json, State};
|
|
|
|
|
use rusqlite::{params, Connection};
|
|
|
|
|
use serde_json::{json, Value};
|
|
|
|
|
|
2024-05-09 11:25:27 +02:00
|
|
|
use crate::{db::DB, timeutil::time_now};
|
2024-04-03 09:49:44 +02:00
|
|
|
|
|
|
|
|
/// Fetch TVL for all tokens
|
|
|
|
|
pub fn get_all_token_tvl(
|
|
|
|
|
connection: &Connection,
|
|
|
|
|
max_timestamp: usize,
|
|
|
|
|
) -> Result<Vec<(String, u64, u64)>> {
|
|
|
|
|
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
|
|
|
|
|
JOIN
|
|
|
|
|
tx ON uf.txid = tx.txid
|
|
|
|
|
WHERE
|
|
|
|
|
us.spent_utxo_hash IS NULL AND
|
|
|
|
|
(tx.mtp_timestamp <= ? OR tx.first_seen_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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get TVL for a single token
|
|
|
|
|
pub fn get_token_tvl(
|
|
|
|
|
connection: &Connection,
|
|
|
|
|
max_timestamp: usize,
|
|
|
|
|
token_id: &str,
|
|
|
|
|
) -> Result<(u64, u64)> {
|
|
|
|
|
let mut statement = connection.prepare(
|
|
|
|
|
"SELECT
|
|
|
|
|
uf.token_amount,
|
|
|
|
|
uf.sats
|
|
|
|
|
FROM
|
|
|
|
|
utxo_funding uf
|
|
|
|
|
LEFT JOIN
|
|
|
|
|
utxo_spending us ON uf.new_utxo_hash = us.spent_utxo_hash
|
|
|
|
|
JOIN
|
|
|
|
|
tx ON uf.txid = tx.txid
|
|
|
|
|
WHERE
|
|
|
|
|
us.spent_utxo_hash IS NULL AND
|
|
|
|
|
(tx.mtp_timestamp <= ? OR tx.first_seen_timestamp <= ?) AND
|
|
|
|
|
uf.token_id = ?
|
|
|
|
|
",
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
// Execute the query with the specified parameters
|
|
|
|
|
let mut query = statement.query(params![max_timestamp, max_timestamp, token_id])?;
|
|
|
|
|
|
|
|
|
|
// Initialize accumulators
|
|
|
|
|
let mut sum_tokens: u64 = 0;
|
|
|
|
|
let mut sum_sats: u64 = 0;
|
|
|
|
|
|
|
|
|
|
// Iterate over the query result
|
|
|
|
|
while let Some(row) = query.next()? {
|
|
|
|
|
let tokens: u64 = row.get(0)?;
|
|
|
|
|
let sats: u64 = row.get(1)?;
|
|
|
|
|
sum_tokens = sum_tokens.checked_add(tokens).context("token overflow")?;
|
|
|
|
|
sum_sats = sum_sats.checked_add(sats).context("sats overflow")?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok((sum_sats, sum_tokens))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deprecated; use valuelocked with optional parameters
|
|
|
|
|
#[get("/tvl/<time>")]
|
2024-05-09 11:25:27 +02:00
|
|
|
pub fn deprecated_tvl(time: usize, conn: &State<DB>) -> Result<Json<Vec<Value>>, Custom<String>> {
|
2024-04-03 09:49:44 +02:00
|
|
|
let db = conn
|
2024-05-09 11:25:27 +02:00
|
|
|
.cauldron_r
|
2024-04-03 09:49:44 +02:00
|
|
|
.get()
|
|
|
|
|
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
|
|
|
|
|
|
|
|
|
|
let tvl: Vec<(String, u64, u64)> = get_all_token_tvl(&db, time)
|
|
|
|
|
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
|
|
|
|
|
|
|
|
|
|
let result: Vec<Value> = tvl
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.map(|(token, token_amount, sats)| {
|
|
|
|
|
json!({
|
|
|
|
|
"token_id": token,
|
|
|
|
|
"token_amount": token_amount,
|
|
|
|
|
"satoshis": sats
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(Json(result))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[get("/valuelocked?<time>")]
|
|
|
|
|
pub fn valuelocked_all(
|
|
|
|
|
time: Option<usize>,
|
2024-05-09 11:25:27 +02:00
|
|
|
conn: &State<DB>,
|
2024-04-03 09:49:44 +02:00
|
|
|
) -> Result<Json<Vec<Value>>, Custom<String>> {
|
2024-05-09 11:25:27 +02:00
|
|
|
let db = conn.cauldron_r.get().map_err(|_| {
|
2024-04-03 09:49:44 +02:00
|
|
|
Custom(
|
|
|
|
|
Status::InternalServerError,
|
|
|
|
|
"Failed to get DB connection".into(),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let time_filter = time.unwrap_or_else(|| time_now() as usize);
|
|
|
|
|
|
|
|
|
|
let tvl: Vec<(String, u64, u64)> = get_all_token_tvl(&db, time_filter)
|
|
|
|
|
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
|
|
|
|
|
|
|
|
|
|
let result: Vec<Value> = tvl
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.map(|(token, token_amount, sats)| {
|
|
|
|
|
json!({
|
|
|
|
|
"token_id": token,
|
|
|
|
|
"token_amount": token_amount,
|
|
|
|
|
"satoshis": sats
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(Json(result))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[get("/valuelocked/<token>?<time>")]
|
|
|
|
|
pub fn valuelocked_token(
|
|
|
|
|
token: &str,
|
|
|
|
|
time: Option<usize>,
|
2024-05-09 11:25:27 +02:00
|
|
|
conn: &State<DB>,
|
2024-04-03 09:49:44 +02:00
|
|
|
) -> Result<Json<Value>, Custom<String>> {
|
2024-05-09 11:25:27 +02:00
|
|
|
let db = conn.cauldron_r.get().map_err(|_| {
|
2024-04-03 09:49:44 +02:00
|
|
|
Custom(
|
|
|
|
|
Status::InternalServerError,
|
|
|
|
|
"Failed to get DB connection".into(),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let time_filter = time.unwrap_or_else(|| time_now() as usize);
|
|
|
|
|
|
|
|
|
|
let (sats, token_amount) = get_token_tvl(&db, time_filter, token)
|
|
|
|
|
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
|
|
|
|
|
|
|
|
|
|
Ok(Json(json!({
|
|
|
|
|
"token_amount": token_amount,
|
|
|
|
|
"satoshis": sats
|
|
|
|
|
})))
|
|
|
|
|
}
|