use std::collections::HashSet;
use tycho_common::Bytes;
use crate::book::levels::Levels;
pub fn tvl_in_usd<'a>(
raw_tvl: f64,
quote_token: &Bytes,
usd_quote_tokens: &HashSet<Bytes>,
priced_pairs: impl IntoIterator<Item = (&'a Bytes, &'a Bytes, &'a Levels)>,
) -> Option<f64> {
if usd_quote_tokens.contains(quote_token) {
return Some(raw_tvl);
}
priced_pairs
.into_iter()
.filter(|(base, quote, _)| *base == quote_token && usd_quote_tokens.contains(*quote))
.find_map(|(_, _, levels)| levels.average_price(1.0))
.map(|price| raw_tvl * price)
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rstest::rstest;
use super::*;
use crate::book::levels::PriceLevel;
const WETH: &str = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const USDC: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
fn address(hex: &str) -> Bytes {
Bytes::from_str(hex).unwrap()
}
fn weth_usdc() -> (Bytes, Bytes, Levels) {
(
address(WETH),
address(USDC),
Levels::new(vec![PriceLevel { quantity: 1.0, price: 3000.0 }]).unwrap(),
)
}
#[rstest]
#[case::quoted_in_a_usd_token(USDC, true, Some(500.0))]
#[case::quoted_in_a_usd_token_without_any_pair(USDC, false, Some(500.0))]
#[case::converted_through_a_priced_pair(WETH, true, Some(1_500_000.0))]
#[case::no_pair_prices_the_quote_token(WETH, false, None)]
fn tvl_is_converted_into_usd_quote_tokens(
#[case] quote_token: &str,
#[case] with_weth_usdc: bool,
#[case] expected: Option<f64>,
) {
let usd_quote_tokens = HashSet::from([address(USDC)]);
let pairs: Vec<_> = with_weth_usdc
.then(weth_usdc)
.into_iter()
.collect();
let normalized = tvl_in_usd(
500.0,
&address(quote_token),
&usd_quote_tokens,
pairs
.iter()
.map(|(base, quote, levels)| (base, quote, levels)),
);
assert_eq!(normalized, expected);
}
}