riftenlabs-indexer/src/rpc/tvl.rs

200 lines
5.1 KiB
Rust
Raw Normal View History

// Copyright (C) 2025 Riften Labs AS
2024-04-03 09:49:44 +02:00
//
// 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
2025-07-18 09:34:22 +02:00
use std::collections::HashMap;
use anyhow::Result;
2024-04-03 09:49:44 +02:00
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use rocket::{get, http::Status, response::status::Custom, serde::json::Json, State};
2025-07-18 09:34:22 +02:00
use rusqlite::Connection;
2024-04-03 09:49:44 +02:00
use serde_json::{json, Value};
2025-07-18 09:34:22 +02:00
use crate::db::{
cauldron::poolvisitor::{
db_visit_pool_entries, OptionalFields, OptionalPoolFields, PoolFilters, PoolVisitor,
},
DB,
};
#[derive(Default)]
struct TvlByTokenVisitor {
tvl: HashMap<String, (u64, u64)>,
}
impl PoolVisitor for TvlByTokenVisitor {
fn optional_fields_wanted(&self) -> u64 {
OptionalPoolFields::TokenId as u64
}
fn visit(&mut self, sats: u64, tokens: u64, optional_fields: OptionalFields) -> Result<bool> {
let token_id = optional_fields.token_id.unwrap();
let entry = self.tvl.entry(token_id).or_insert((0u64, 0u64));
entry.0 += sats;
entry.1 += tokens;
Ok(true)
}
}
#[derive(Default)]
struct TvlVisitor {
sats: u64,
tokens: u64,
}
impl PoolVisitor for TvlVisitor {
fn optional_fields_wanted(&self) -> u64 {
0
}
2024-04-03 09:49:44 +02:00
2025-07-18 09:34:22 +02:00
fn visit(&mut self, sats: u64, tokens: u64, _optional_fields: OptionalFields) -> Result<bool> {
self.sats += sats;
self.tokens += tokens;
Ok(true)
}
}
#[derive(Default)]
struct TvlSatsOnlyVisitor {
sats: u64,
}
impl PoolVisitor for TvlSatsOnlyVisitor {
fn optional_fields_wanted(&self) -> u64 {
0
}
fn visit(&mut self, sats: u64, _tokens: u64, _optional_fields: OptionalFields) -> Result<bool> {
self.sats += sats;
Ok(true)
}
}
/// Fetch TVL for all tokens by token id (deprecated)
pub fn deprecated_get_all_token_tvl(
2024-04-03 09:49:44 +02:00
connection: &Connection,
max_timestamp: usize,
2025-07-18 09:34:22 +02:00
) -> Result<HashMap<String, (u64, u64)>> {
let mut visitor = TvlByTokenVisitor::default();
db_visit_pool_entries(
connection,
&mut visitor,
PoolFilters {
timestamp_less_than: Some(max_timestamp as u64),
token_id: None,
owner: None,
},
2024-04-03 09:49:44 +02:00
)?;
2025-07-18 09:34:22 +02:00
Ok(visitor.tvl)
}
/// Fetch sats side of TVL for all tokens
pub fn get_total_sats_tvl(connection: &Connection, max_timestamp: Option<usize>) -> Result<u64> {
let mut visitor = TvlSatsOnlyVisitor::default();
db_visit_pool_entries(
connection,
&mut visitor,
PoolFilters {
timestamp_less_than: max_timestamp.map(|t| t as u64),
token_id: None,
owner: None,
},
)?;
2024-04-03 09:49:44 +02:00
2025-07-18 09:34:22 +02:00
Ok(visitor.sats)
2024-04-03 09:49:44 +02:00
}
/// Get TVL for a single token
pub fn get_token_tvl(
connection: &Connection,
2025-07-18 09:34:22 +02:00
max_timestamp: Option<usize>,
2024-04-03 09:49:44 +02:00
token_id: &str,
) -> Result<(u64, u64)> {
2025-07-18 09:34:22 +02:00
let mut visitor = TvlVisitor::default();
db_visit_pool_entries(
connection,
&mut visitor,
PoolFilters {
timestamp_less_than: max_timestamp.map(|t| t as u64),
token_id: Some(token_id.to_string()),
owner: None,
},
2024-04-03 09:49:44 +02:00
)?;
2025-07-18 09:34:22 +02:00
Ok((visitor.sats, visitor.tokens))
2024-04-03 09:49:44 +02:00
}
// Deprecated; use valuelocked with optional parameters
2025-07-18 09:34:22 +02:00
// used by defilama; fix adapter first
2024-04-03 09:49:44 +02:00
#[get("/tvl/<time>")]
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
.cauldron_r
2024-04-03 09:49:44 +02:00
.get()
2025-07-16 09:31:14 +02:00
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {e}")))?;
2024-04-03 09:49:44 +02:00
2025-07-18 09:34:22 +02:00
let tvl: HashMap<String, (u64, u64)> = deprecated_get_all_token_tvl(&db, time)
2025-07-16 09:31:14 +02:00
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {e}")))?;
2024-04-03 09:49:44 +02:00
let result: Vec<Value> = tvl
.into_par_iter()
2025-07-18 09:34:22 +02:00
.map(|(token, (sats, token_amount))| {
2024-04-03 09:49:44 +02:00
json!({
"token_id": token,
2025-07-18 09:34:22 +02:00
"satoshis": sats,
2024-04-03 09:49:44 +02:00
"token_amount": token_amount,
})
})
.collect();
Ok(Json(result))
}
#[get("/valuelocked?<time>")]
pub fn valuelocked_all(
time: Option<usize>,
conn: &State<DB>,
2025-07-18 09:34:22 +02:00
) -> Result<Json<Value>, Custom<String>> {
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(),
)
})?;
2025-07-18 09:34:22 +02:00
let sats: u64 = get_total_sats_tvl(&db, time)
2025-07-16 09:31:14 +02:00
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {e}")))?;
2024-04-03 09:49:44 +02:00
2025-07-18 09:34:22 +02:00
Ok(Json(json!({
"satoshis": sats
})))
2024-04-03 09:49:44 +02:00
}
#[get("/valuelocked/<token>?<time>")]
pub fn valuelocked_token(
token: &str,
time: Option<usize>,
conn: &State<DB>,
2024-04-03 09:49:44 +02:00
) -> Result<Json<Value>, Custom<String>> {
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(),
)
})?;
2025-07-18 09:34:22 +02:00
let (sats, token_amount) = get_token_tvl(&db, time, token)
2025-07-16 09:31:14 +02:00
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {e}")))?;
2024-04-03 09:49:44 +02:00
Ok(Json(json!({
"token_amount": token_amount,
"satoshis": sats
})))
}