From c802f12cc72b4583c52473f029ad0d5d21a08807 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 8 Jan 2026 13:43:44 -0500 Subject: [PATCH 1/3] Add TVL endpoint for DefiLlama --- althea-info-server/src/config.rs | 183 +++++++++++++ althea-info-server/src/endpoints.rs | 65 +++++ althea-info-server/src/main.rs | 68 +---- althea-info-server/src/total_suppy.rs | 11 +- althea-info-server/src/tvl.rs | 353 ++++++++++++++++++++++++++ 5 files changed, 620 insertions(+), 60 deletions(-) create mode 100644 althea-info-server/src/config.rs create mode 100644 althea-info-server/src/endpoints.rs create mode 100644 althea-info-server/src/tvl.rs diff --git a/althea-info-server/src/config.rs b/althea-info-server/src/config.rs new file mode 100644 index 0000000..e055565 --- /dev/null +++ b/althea-info-server/src/config.rs @@ -0,0 +1,183 @@ +use std::collections::HashMap; + +use clarity::Address; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[allow(non_snake_case)] +pub struct Token { + /// The Althea L1 EVM address of the token + pub althea_evm_address: Address, + /// The denom of this token on Althea L1 on the Cosmos layer (IBC tokens will have ibc/ prefix) + pub althea_denom: String, + /// The erc20 address of this token on Ethereum, may be null if this token does not exist there + pub eth_address: Option
, + /// the source ibc channel of this token, may be null if this token is not an IBC token + /// IBC port is assumed to be transfer for all tokens + pub ibc_channel: Option, + /// The number of decimal places the token uses + pub decimals: u32, + /// The name of the token (multiple words) + pub name: String, + /// The symbol of the token (small number of letters) + pub symbol: String, + /// The coingecko ID of this token for price lookups, essential for the DefiLlama integration + pub coingecko_id: String, +} + +pub fn get_token(token: &str) -> Option { + let pools = get_tokens(); + pools + .values() + .find(|t| { + t.althea_evm_address.to_string() == token + || { t.althea_denom == token } + || { + if let Some(eth_address) = &t.eth_address { + eth_address.to_string() == token + } else { + false + } + } + || t.name == token + || t.symbol == token + }) + .cloned() +} + +pub fn get_tokens() -> HashMap { + let mut tokens = HashMap::new(); + let althea = Token { + althea_evm_address: "0x0000000000000000000000000000000000000000" + .parse() + .unwrap(), + althea_denom: "aalthea".to_string(), + ibc_channel: Some("channel-0".to_string()), + eth_address: Some( + "0xF9e595BC0aF20cfa1561dfE085E3DE9Fcf9Fbfa2" + .parse() + .unwrap(), + ), + decimals: 18, + name: "ALTHEA".to_string(), + symbol: "ALTHEA".to_string(), + coingecko_id: "althea".to_string(), + }; + tokens.insert(althea.symbol.clone(), althea); + + let usdc = Token { + althea_evm_address: "0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd" + .parse() + .unwrap(), + eth_address: Some( + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + .parse() + .unwrap(), + ), + althea_denom: "ibc/17CD484EE7D9723B847D95015FA3EBD1572FD13BC84FB838F55B18A57450F25B" + .to_string(), + ibc_channel: Some("channel-0".to_string()), + decimals: 6, + name: "Circle USD Stablecoin".to_string(), + symbol: "USDC".to_string(), + coingecko_id: "usdc".to_string(), + }; + tokens.insert(usdc.symbol.clone(), usdc); + + let usdt = Token { + althea_evm_address: "0xecEEEfCEE421D8062EF8d6b4D814efe4dc898265" + .parse() + .unwrap(), + eth_address: Some( + "0xdAC17F958D2ee523a2206206994597C13D831ec7" + .parse() + .unwrap(), + ), + althea_denom: "ibc/4F6A2DEFEA52CD8D90966ADCB2BD0593D3993AB0DF7F6AEB3EFD6167D79237B0" + .to_string(), + ibc_channel: Some("channel-0".to_string()), + decimals: 6, + name: "Tether Stablecoin".to_string(), + symbol: "USDT".to_string(), + coingecko_id: "tether".to_string(), + }; + tokens.insert(usdt.symbol.clone(), usdt); + + let usds = Token { + althea_evm_address: "0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75" + .parse() + .unwrap(), + althea_denom: "ibc/AE1B617F7F329ED83C20AC584B03579EEFE3322EF601CE88936A0271BE1157DD" + .to_string(), + eth_address: Some( + "0xdC035D45d973E3EC169d2276DDab16f1e407384F" + .parse() + .unwrap(), + ), + ibc_channel: Some("channel-0".to_string()), + decimals: 18, + name: "USDS".to_string(), + symbol: "USDS".to_string(), + coingecko_id: "usds".to_string(), + }; + tokens.insert(usds.symbol.clone(), usds); + + let susds = Token { + althea_evm_address: "0x5FD55A1B9FC24967C4dB09C513C3BA0DFa7FF687" + .parse() + .unwrap(), + eth_address: Some( + "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD" + .parse() + .unwrap(), + ), + althea_denom: "ibc/576150049104D47DFD447482EEED2FC8B44AB0D9A772D673717062B49D9820C5" + .to_string(), + ibc_channel: Some("channel-0".to_string()), + decimals: 18, + name: "Savings USDS".to_string(), + symbol: "sUSDS".to_string(), + coingecko_id: "susds".to_string(), + }; + tokens.insert(susds.symbol.clone(), susds); + + let grav = Token { + althea_evm_address: "0x1D54EcB8583Ca25895c512A8308389fFD581F9c9" + .parse() + .unwrap(), + eth_address: Some( + "0x9f2ef66a09A5d2dAB13D84A7638668EA36679e03" + .parse() + .unwrap(), + ), + althea_denom: "ibc/FC9D92EC12BC974E8B6179D411351524CD5C2EBC3CE29D5BA856414FEFA47093" + .to_string(), + ibc_channel: Some("channel-0".to_string()), + decimals: 6, + name: "Graviton".to_string(), + symbol: "GRAV".to_string(), + coingecko_id: "graviton".to_string(), + }; + tokens.insert(grav.symbol.clone(), grav); + + let weth = Token { + althea_evm_address: "0xc03345448969Dd8C00e9E4A85d2d9722d093aF8E" + .parse() + .unwrap(), + eth_address: Some( + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" + .parse() + .unwrap(), + ), + althea_denom: "ibc/DC186CA7A8C009B43774EBDC825C935CABA9743504CE6037507E6E5CCE12858A" + .to_string(), + ibc_channel: Some("channel-0".to_string()), + decimals: 18, + name: "Ethereum".to_string(), + symbol: "WETH".to_string(), + coingecko_id: "weth".to_string(), + }; + tokens.insert(weth.symbol.clone(), weth); + + tokens +} diff --git a/althea-info-server/src/endpoints.rs b/althea-info-server/src/endpoints.rs new file mode 100644 index 0000000..d541b22 --- /dev/null +++ b/althea-info-server/src/endpoints.rs @@ -0,0 +1,65 @@ +use actix_web::{get, HttpResponse, Responder}; +use log::error; + +use crate::{total_suppy::get_supply_info, tvl::get_unpriced_tvl, ALTHEA_NODE_GRPC}; + +#[get("/total_supply")] +async fn endpoint_get_total_supply() -> impl Responder { + // if we have already computed supply info return it, if not return an error + match get_supply_info() { + Some(v) => HttpResponse::Ok().json(v.total_supply), + None => HttpResponse::InternalServerError() + .json("Info not yet generated, please query in 5 minutes"), + } +} + +/// If the liquid supply is lower than this value it's stale or otherwise invalid and we should +/// return an error. +pub const SUPPLY_CHECKPOINT: u128 = 500000000000000; +#[get("/total_liquid_supply")] +async fn endpoint_get_total_liquid_supply() -> impl Responder { + // if we have already computed supply info return it, if not return an error + match get_supply_info() { + Some(v) => { + if v.total_liquid_supply > SUPPLY_CHECKPOINT.into() { + HttpResponse::Ok().json(v.total_liquid_supply) + } else { + error!("Invalid supply data, got total liquid supply of {:#?}", v); + HttpResponse::InternalServerError() + .json("Invalid supply data, Althea fullnode is stale") + } + } + None => HttpResponse::InternalServerError() + .json("Info not yet generated, please query in 5 minutes"), + } +} + +#[get("/supply_info")] +async fn endpoint_get_all_supply_info() -> impl Responder { + // if we have already computed supply info return it, if not return an error + match get_supply_info() { + Some(v) => { + if v.total_liquid_supply > SUPPLY_CHECKPOINT.into() { + HttpResponse::Ok().json(v) + } else { + error!("Invalid supply data, got total liquid supply of {:#?}", v); + HttpResponse::InternalServerError() + .json("Invalid supply data, Althea fullnode is stale") + } + } + None => HttpResponse::InternalServerError() + .json("Info not yet generated, please query in 5 minutes"), + } +} + +#[get("/unpriced_tvl")] +async fn endpoint_get_unpriced_tvl() -> impl Responder { + // if we have already computed supply info return it, if not return an error + match get_unpriced_tvl(ALTHEA_NODE_GRPC.to_string()).await { + Ok(v) => HttpResponse::Ok().json(v), + Err(e) => { + error!("Error getting unpriced TVL: {:#?}", e); + HttpResponse::InternalServerError().json("Error getting unpriced TVL") + } + } +} diff --git a/althea-info-server/src/main.rs b/althea-info-server/src/main.rs index d8799ae..e9e8f48 100644 --- a/althea-info-server/src/main.rs +++ b/althea-info-server/src/main.rs @@ -1,8 +1,11 @@ #[macro_use] extern crate lazy_static; +pub mod config; +pub mod endpoints; pub mod tls; pub mod total_suppy; +pub mod tvl; const DEVELOPMENT: bool = cfg!(feature = "development"); const SSL: bool = !DEVELOPMENT; @@ -15,13 +18,16 @@ const DOMAIN: &str = if cfg!(test) || DEVELOPMENT { const INFO_SERVER_PORT: u16 = 9000; use crate::{ + endpoints::{ + endpoint_get_all_supply_info, endpoint_get_total_liquid_supply, endpoint_get_total_supply, + endpoint_get_unpriced_tvl, + }, tls::{load_certs, load_private_key}, - total_suppy::get_supply_info, }; use actix_cors::Cors; -use actix_web::{get, App, HttpResponse, HttpServer, Responder}; +use actix_web::{App, HttpServer}; use env_logger::Env; -use log::{error, info}; +use log::info; use rustls::ServerConfig; use total_suppy::chain_total_supply_thread; @@ -29,55 +35,6 @@ pub const ALTHEA_NODE_GRPC: &str = "https://rpc.althea.zone:9090"; pub const ALTHEA_PREFIX: &str = "althea"; pub const REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); -#[get("/total_supply")] -async fn get_total_supply() -> impl Responder { - // if we have already computed supply info return it, if not return an error - match get_supply_info() { - Some(v) => HttpResponse::Ok().json(v.total_supply), - None => HttpResponse::InternalServerError() - .json("Info not yet generated, please query in 5 minutes"), - } -} - -/// If the liquid supply is lower than this value it's stale or otherwise invalid and we should -/// return an error. -pub const SUPPLY_CHECKPOINT: u128 = 500000000000000; -#[get("/total_liquid_supply")] -async fn get_total_liquid_supply() -> impl Responder { - // if we have already computed supply info return it, if not return an error - match get_supply_info() { - Some(v) => { - if v.total_liquid_supply > SUPPLY_CHECKPOINT.into() { - HttpResponse::Ok().json(v.total_liquid_supply) - } else { - error!("Invalid supply data, got total liquid supply of {:#?}", v); - HttpResponse::InternalServerError() - .json("Invalid supply data, Althea fullnode is stale") - } - } - None => HttpResponse::InternalServerError() - .json("Info not yet generated, please query in 5 minutes"), - } -} - -#[get("/supply_info")] -async fn get_all_supply_info() -> impl Responder { - // if we have already computed supply info return it, if not return an error - match get_supply_info() { - Some(v) => { - if v.total_liquid_supply > SUPPLY_CHECKPOINT.into() { - HttpResponse::Ok().json(v) - } else { - error!("Invalid supply data, got total liquid supply of {:#?}", v); - HttpResponse::InternalServerError() - .json("Invalid supply data, Althea fullnode is stale") - } - } - None => HttpResponse::InternalServerError() - .json("Info not yet generated, please query in 5 minutes"), - } -} - #[actix_web::main] async fn main() -> std::io::Result<()> { openssl_probe::init_ssl_cert_env_vars(); @@ -93,9 +50,10 @@ async fn main() -> std::io::Result<()> { .allow_any_header() .allow_any_method(), ) - .service(get_total_supply) - .service(get_total_liquid_supply) - .service(get_all_supply_info) + .service(endpoint_get_total_supply) + .service(endpoint_get_total_liquid_supply) + .service(endpoint_get_all_supply_info) + .service(endpoint_get_unpriced_tvl) }); let info_server = if SSL { diff --git a/althea-info-server/src/total_suppy.rs b/althea-info-server/src/total_suppy.rs index b724ec3..9a22d7a 100644 --- a/althea-info-server/src/total_suppy.rs +++ b/althea-info-server/src/total_suppy.rs @@ -200,11 +200,12 @@ async fn compute_liquid_supply( // Account for tokens staked beyond original vesting (e.g., staked rewards) // These are fully liquid and not tracked by the vesting module - let staked_beyond_vesting = if user.total_staked > total_delegated_vesting + total_delegated_free { - user.total_staked - total_delegated_vesting - total_delegated_free - } else { - 0u8.into() - }; + let staked_beyond_vesting = + if user.total_staked > total_delegated_vesting + total_delegated_free { + user.total_staked - total_delegated_vesting - total_delegated_free + } else { + 0u8.into() + }; total_liquid_supply += staked_beyond_vesting; total_nonvesting_staked += staked_beyond_vesting; diff --git a/althea-info-server/src/tvl.rs b/althea-info-server/src/tvl.rs new file mode 100644 index 0000000..ca50d78 --- /dev/null +++ b/althea-info-server/src/tvl.rs @@ -0,0 +1,353 @@ +use cosmos_sdk_proto_althea::cosmos::{ + bank::v1beta1::{query_client::QueryClient as BankQueryClient, QueryTotalSupplyRequest}, + base::{query::v1beta1::PageRequest, v1beta1::Coin}, +}; +use cosmos_sdk_proto_althea::ibc::applications::transfer::v1::{ + query_client::QueryClient as IbcTransferQueryClient, QueryEscrowAddressRequest, +}; +use num256::Uint256; +use rust_decimal::prelude::Zero; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use tokio::time::sleep; + +use crate::{ + config::{get_token, get_tokens, Token}, + total_suppy::get_supply_info, +}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Tvl { + pub althea_on_chain: TokenAmount, + pub ibc_tokens_on_chain: Vec, + pub althea_native_erc20s_on_chain: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TokenAmount { + pub token: Token, + pub amount: Uint256, +} + +/// Type alias for the total supply result from the Cosmos bank module +pub type TotalSupply = Vec; + +pub const ALTHEA_TOKEN_DENOM: &str = "aalthea"; + +// Fetches and computes the supply of bridged IBC tokens, native althea (from the total supply thread info), and altheaL1-native erc20s +pub async fn get_unpriced_tvl(grpc: String) -> Result { + let supply = get_total_supply(&grpc).await?; + let supply = filter_supply_by_tokens(supply); + let tokens_on_chain = get_tokens_on_chain(&supply, &grpc).await?; + let althea_supply = + get_supply_info().map_or_else(Uint256::zero, |info| info.total_liquid_supply); + + let althea_on_chain = TokenAmount { + token: get_token(ALTHEA_TOKEN_DENOM).unwrap(), + amount: althea_supply, + }; + + let ibc_tokens_on_chain: Vec = tokens_on_chain + .clone() + .into_iter() + .filter_map(|(t, v)| { + if t.althea_denom.starts_with("ibc/") { + return Some(TokenAmount { + token: t, + amount: v, + }); + } + None + }) + .collect(); + + let althea_native_erc20s_on_chain: Vec = tokens_on_chain + .clone() + .into_iter() + .filter_map(|(t, v)| { + if !t.althea_denom.starts_with("ibc/") && t.althea_denom != ALTHEA_TOKEN_DENOM { + return Some(TokenAmount { + token: t, + amount: v, + }); + } + None + }) + .collect(); + let tvl = Tvl { + althea_on_chain, + ibc_tokens_on_chain, + althea_native_erc20s_on_chain, + }; + Ok(tvl) +} + +pub fn filter_supply_by_tokens(supply: TotalSupply) -> TotalSupply { + let tokens = get_tokens(); + supply + .into_iter() + .filter(|coin| { + tokens + .values() + .any(|token| token.althea_denom == coin.denom) + }) + .collect() +} + +/// Gets the total supply of all tokens from the Cosmos bank module +pub async fn get_total_supply(grpc: &str) -> Result { + let mut next_key: Option> = None; + let mut all_supply = Vec::new(); + + // Create a client and connect to the appropriate URL + let mut client = BankQueryClient::connect(grpc.to_string()) + .await + .map_err(|e| format!("Failed to connect to gRPC endpoint: {e}"))?; + + // Loop until we've retrieved all supply entries + loop { + let request = QueryTotalSupplyRequest { + pagination: Some(PageRequest { + key: next_key.unwrap_or_default(), + offset: 0, + limit: 100, // Request a reasonable batch size + count_total: false, + reverse: false, + }), + }; + + // Retry logic for the total_supply request + const MAX_RETRIES: u32 = 3; + let mut response = None; + + for attempt in 1..=MAX_RETRIES { + match client.total_supply(request.clone()).await { + Ok(resp) => { + response = Some(resp); + break; + } + Err(e) => { + if attempt < MAX_RETRIES { + log::warn!( + "Failed to query total supply (attempt {}/{}): {}, retrying...", + attempt, + MAX_RETRIES, + e + ); + sleep(tokio::time::Duration::from_millis(100 * attempt as u64)).await; + } else { + return Err(format!( + "Error querying total supply after {} attempts: {}", + MAX_RETRIES, e + )); + } + } + } + } + + let response_inner = response + .expect("Response should be Some after successful retry") + .into_inner(); + + // Add supply from this page to our result + all_supply.extend(response_inner.supply); + + // Check if there are more pages + if let Some(page_response) = response_inner.pagination { + if !page_response.next_key.is_empty() { + next_key = Some(page_response.next_key); + } else { + break; // No more pages + } + } else { + break; // No pagination response means no more pages + } + } + + Ok(all_supply) +} + +pub fn get_ibc_tokens_bridged_in(supply: &TotalSupply) -> HashMap { + let mut result = HashMap::new(); + + for (_token_name, token) in get_tokens() { + if token.althea_denom.starts_with("ibc/") { + result.insert(token.clone(), Uint256::zero()); + } + } + + // Filter for IBC tokens (denoms starting with "ibc/") + for coin in supply { + if coin.denom.starts_with("ibc/") { + // Find the token in our result map that matches this denom + for (token, amount) in result.iter_mut() { + if token.althea_denom == coin.denom { + // Parse the coin amount and add it to the existing value + match coin.amount.parse::() { + Ok(parsed_amount) => { + *amount += parsed_amount; + } + Err(e) => { + log::error!( + "Failed to parse amount '{}' for denom {}: {}", + coin.amount, + coin.denom, + e + ); + } + } + break; + } + } + } + } + + result +} + +pub async fn get_tokens_on_chain( + supply: &TotalSupply, + grpc: &str, +) -> Result, String> { + let mut result = HashMap::new(); + + // Create clients for IBC transfer and bank queries + let mut ibc_client = IbcTransferQueryClient::connect(grpc.to_string()) + .await + .map_err(|e| format!("Failed to connect to IBC transfer gRPC endpoint: {e}"))?; + + let ibc_port = "transfer".to_string(); + let channel_0_id = "channel-0".to_string(); + let mut channel0_escrow_address: String = String::new(); + const MAX_RETRIES: u32 = 3; + for _ in 0..MAX_RETRIES { + if let Ok(response) = ibc_client + .escrow_address(QueryEscrowAddressRequest { + port_id: ibc_port.clone(), + channel_id: channel_0_id.clone(), + }) + .await + { + channel0_escrow_address = response.into_inner().escrow_address; + break; + } + } + if channel0_escrow_address.is_empty() { + return Err("Failed to get escrow address for channel-0 after retries".to_string()); + } + + let mut bank_client = BankQueryClient::connect(grpc.to_string()) + .await + .map_err(|e| format!("Failed to connect to bank gRPC endpoint: {e}"))?; + + // Process each coin in the supply + for coin in supply { + // Find the matching token from our config + let token = get_token(&coin.denom); + + if let Some(token) = token { + // Parse the total supply amount + let total_supply: Uint256 = match coin.amount.parse() { + Ok(amount) => amount, + Err(e) => { + log::error!( + "Failed to parse supply amount '{}' for denom {}: {}", + coin.amount, + coin.denom, + e + ); + continue; + } + }; + + // Get the escrow address for this token + // Standard IBC transfer port is "transfer", channel needs to come from token config + // TODO: Get channel_id from token configuration + let port_id = "transfer".to_string(); + let channel_id = if let Some(ref channel) = token.ibc_channel { + channel.clone() + } else { + // If no channel configured, assume all tokens are on-chain (no escrow) + result.insert(token.clone(), total_supply); + continue; + }; + + let escrow_address: String = if port_id == ibc_port && channel_id == channel_0_id { + channel0_escrow_address.clone() + } else { + let escrow_request = QueryEscrowAddressRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + + match ibc_client.escrow_address(escrow_request).await { + Ok(response) => response.into_inner().escrow_address, + Err(e) => { + log::warn!( + "Failed to get escrow address for {}/{}: {}, assuming no escrow", + port_id, + channel_id, + e + ); + // If we can't get escrow address, assume all tokens are on-chain + result.insert(token.clone(), total_supply); + continue; + } + } + }; + + // Query the balance at the escrow address for this denom + let balance_request = + cosmos_sdk_proto_althea::cosmos::bank::v1beta1::QueryBalanceRequest { + address: escrow_address.clone(), + denom: coin.denom.clone(), + }; + + let escrowed_amount: Uint256 = match bank_client.balance(balance_request).await { + Ok(response) => { + if let Some(balance) = response.into_inner().balance { + match balance.amount.parse() { + Ok(amount) => amount, + Err(e) => { + log::error!( + "Failed to parse escrowed amount '{}' for denom {}: {}", + balance.amount, + coin.denom, + e + ); + Uint256::zero() + } + } + } else { + Uint256::zero() + } + } + Err(e) => { + log::warn!( + "Failed to query balance for escrow address {}: {}, assuming zero escrow", + escrow_address, + e + ); + Uint256::zero() + } + }; + + // Calculate on-chain balance: total supply - escrowed amount + let on_chain_amount = if total_supply >= escrowed_amount { + total_supply - escrowed_amount + } else { + log::error!( + "Escrowed amount ({}) exceeds total supply ({}) for denom {}", + escrowed_amount, + total_supply, + coin.denom + ); + Uint256::zero() + }; + + result.insert(token.clone(), on_chain_amount); + } + } + + Ok(result) +} From ae29ff9f02597eb2664b581a60e730615b1f58d3 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 8 Jan 2026 15:21:57 -0500 Subject: [PATCH 2/3] Implement PR suggestions --- althea-info-server/src/config.rs | 6 +-- althea-info-server/src/endpoints.rs | 2 +- althea-info-server/src/tvl.rs | 81 ++++++----------------------- 3 files changed, 19 insertions(+), 70 deletions(-) diff --git a/althea-info-server/src/config.rs b/althea-info-server/src/config.rs index e055565..34cd227 100644 --- a/althea-info-server/src/config.rs +++ b/althea-info-server/src/config.rs @@ -26,12 +26,12 @@ pub struct Token { } pub fn get_token(token: &str) -> Option { - let pools = get_tokens(); - pools + let tokens = get_tokens(); + tokens .values() .find(|t| { t.althea_evm_address.to_string() == token - || { t.althea_denom == token } + || t.althea_denom == token || { if let Some(eth_address) = &t.eth_address { eth_address.to_string() == token diff --git a/althea-info-server/src/endpoints.rs b/althea-info-server/src/endpoints.rs index d541b22..fd74012 100644 --- a/althea-info-server/src/endpoints.rs +++ b/althea-info-server/src/endpoints.rs @@ -54,7 +54,7 @@ async fn endpoint_get_all_supply_info() -> impl Responder { #[get("/unpriced_tvl")] async fn endpoint_get_unpriced_tvl() -> impl Responder { - // if we have already computed supply info return it, if not return an error + // Try to get the TVL, on failure return an error match get_unpriced_tvl(ALTHEA_NODE_GRPC.to_string()).await { Ok(v) => HttpResponse::Ok().json(v), Err(e) => { diff --git a/althea-info-server/src/tvl.rs b/althea-info-server/src/tvl.rs index ca50d78..0246c86 100644 --- a/althea-info-server/src/tvl.rs +++ b/althea-info-server/src/tvl.rs @@ -47,33 +47,21 @@ pub async fn get_unpriced_tvl(grpc: String) -> Result { amount: althea_supply, }; - let ibc_tokens_on_chain: Vec = tokens_on_chain - .clone() - .into_iter() - .filter_map(|(t, v)| { - if t.althea_denom.starts_with("ibc/") { - return Some(TokenAmount { - token: t, - amount: v, - }); - } - None - }) - .collect(); - - let althea_native_erc20s_on_chain: Vec = tokens_on_chain - .clone() - .into_iter() - .filter_map(|(t, v)| { - if !t.althea_denom.starts_with("ibc/") && t.althea_denom != ALTHEA_TOKEN_DENOM { - return Some(TokenAmount { - token: t, - amount: v, - }); - } - None - }) - .collect(); + let mut ibc_tokens_on_chain: Vec = Vec::new(); + let mut althea_native_erc20s_on_chain: Vec = Vec::new(); + for (t, v) in tokens_on_chain { + if t.althea_denom.starts_with("ibc/") { + ibc_tokens_on_chain.push(TokenAmount { + token: t, + amount: v, + }); + } else if t.althea_denom != ALTHEA_TOKEN_DENOM { + althea_native_erc20s_on_chain.push(TokenAmount { + token: t, + amount: v, + }); + } + } let tvl = Tvl { althea_on_chain, ibc_tokens_on_chain, @@ -167,44 +155,6 @@ pub async fn get_total_supply(grpc: &str) -> Result { Ok(all_supply) } -pub fn get_ibc_tokens_bridged_in(supply: &TotalSupply) -> HashMap { - let mut result = HashMap::new(); - - for (_token_name, token) in get_tokens() { - if token.althea_denom.starts_with("ibc/") { - result.insert(token.clone(), Uint256::zero()); - } - } - - // Filter for IBC tokens (denoms starting with "ibc/") - for coin in supply { - if coin.denom.starts_with("ibc/") { - // Find the token in our result map that matches this denom - for (token, amount) in result.iter_mut() { - if token.althea_denom == coin.denom { - // Parse the coin amount and add it to the existing value - match coin.amount.parse::() { - Ok(parsed_amount) => { - *amount += parsed_amount; - } - Err(e) => { - log::error!( - "Failed to parse amount '{}' for denom {}: {}", - coin.amount, - coin.denom, - e - ); - } - } - break; - } - } - } - } - - result -} - pub async fn get_tokens_on_chain( supply: &TotalSupply, grpc: &str, @@ -262,7 +212,6 @@ pub async fn get_tokens_on_chain( // Get the escrow address for this token // Standard IBC transfer port is "transfer", channel needs to come from token config - // TODO: Get channel_id from token configuration let port_id = "transfer".to_string(); let channel_id = if let Some(ref channel) = token.ibc_channel { channel.clone() From 65b53f7f588207a6cd4623ee537cee3ead67ec61 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 8 Jan 2026 15:25:39 -0500 Subject: [PATCH 3/3] Remove gravity references, replace with althea --- .github/workflows/automated-deploy.yml | 2 +- .github/workflows/ci.yml | 2 +- althea-info-dash/public/manifest.json | 4 ++-- althea-info-server/src/total_suppy.rs | 4 ++-- scripts/deploy-info-server.yml | 6 +++--- scripts/hosts | 2 +- scripts/roles/setup-info-server/tasks/systemd.yml | 8 ++++---- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/automated-deploy.yml b/.github/workflows/automated-deploy.yml index 80c16d6..11c0eeb 100644 --- a/.github/workflows/automated-deploy.yml +++ b/.github/workflows/automated-deploy.yml @@ -11,7 +11,7 @@ env: jobs: build-and-deploy: - name: Build and deploy Gravity Info Server + name: Build and deploy Althea Info Server runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d07f049..60d6a3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Tests for Gravity Info +name: Tests for Althea Info on: push: diff --git a/althea-info-dash/public/manifest.json b/althea-info-dash/public/manifest.json index 4f10115..93c0509 100644 --- a/althea-info-dash/public/manifest.json +++ b/althea-info-dash/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "Gravity Info", - "name": "Gravity Information", + "short_name": "Althea Info", + "name": "Althea Information", "icons": [ { "src": "favicon.ico", diff --git a/althea-info-server/src/total_suppy.rs b/althea-info-server/src/total_suppy.rs index 9a22d7a..e0f82a0 100644 --- a/althea-info-server/src/total_suppy.rs +++ b/althea-info-server/src/total_suppy.rs @@ -32,9 +32,9 @@ pub const ALTHEA_DENOM: &str = "aalthea"; #[derive(Debug, Clone, Serialize)] pub struct ChainTotalSupplyNumbers { - /// The total amount of Graviton tokens currently in existance including those vesting and in the community pool + /// The total amount of Althea tokens currently in existance including those vesting and in the community pool pub total_supply: Uint256, - /// The total amount of Gravition in the community pool + /// The total amount of Althea in the community pool pub community_pool: Uint256, /// All tokens that are 'liquid' meaning in a balance, claimable now as rewards /// or staked and eligeable to withdraw and spend, essentially just exludes vesting diff --git a/scripts/deploy-info-server.yml b/scripts/deploy-info-server.yml index ce76ebc..826d67c 100644 --- a/scripts/deploy-info-server.yml +++ b/scripts/deploy-info-server.yml @@ -1,7 +1,7 @@ -# Sets up the gravity info server plus an nginx server to host the frontend +# Sets up the Althea info server plus an nginx server to host the frontend -- name: Install Gravity Info server - hosts: gravity_info_server +- name: Install Althea Info server + hosts: althea_info_server user: root roles: - install-deps diff --git a/scripts/hosts b/scripts/hosts index 3a11a60..0cec5d2 100644 --- a/scripts/hosts +++ b/scripts/hosts @@ -1,2 +1,2 @@ -[gravity_info_server] +[althea_info_server] info.althea.zone diff --git a/scripts/roles/setup-info-server/tasks/systemd.yml b/scripts/roles/setup-info-server/tasks/systemd.yml index 224c148..136d307 100644 --- a/scripts/roles/setup-info-server/tasks/systemd.yml +++ b/scripts/roles/setup-info-server/tasks/systemd.yml @@ -21,13 +21,13 @@ command: systemctl status althea-info-server ignore_errors: true changed_when: false - register: service_gravity_info_server_status + register: service_althea_info_server_status - name: Report status of Althea info server fail: msg: | Service althea-info-server is not running. Output of `systemctl status althea-info-server`: - {{ service_gravity_info_server_status.stdout }} - {{ service_gravity_info_server_status.stderr }} - when: service_gravity_info_server_status is failed \ No newline at end of file + {{ service_althea_info_server_status.stdout }} + {{ service_althea_info_server_status.stderr }} + when: service_althea_info_server_status is failed \ No newline at end of file