use solana_pubkey::Pubkey as PhoenixPubkey;
pub fn fmt_price(v: f64, decimals: usize) -> String {
let s = format!("{:.prec$}", v, prec = decimals);
let (integer, decimal) = s.split_once('.').unwrap_or((&s, ""));
let negative = integer.starts_with('-');
let digits: &str = if negative { &integer[1..] } else { integer };
let with_commas: String = digits
.as_bytes()
.rchunks(3)
.rev()
.map(|chunk| std::str::from_utf8(chunk).expect("integer part of formatted float is ASCII"))
.collect::<Vec<_>>()
.join(",");
if decimal.is_empty() {
if negative {
format!("-{}", with_commas)
} else {
with_commas
}
} else if negative {
format!("-{}.{}", with_commas, decimal)
} else {
format!("{}.{}", with_commas, decimal)
}
}
pub fn fmt_size(v: f64, decimals: usize) -> String {
format!("{:.prec$}", v, prec = decimals)
}
pub fn fmt_compact_prec(v: f64, prec: usize) -> String {
let abs = v.abs();
let (scaled, suffix) = if abs >= 1_000_000_000.0 {
(v / 1_000_000_000.0, "B")
} else if abs >= 1_000_000.0 {
(v / 1_000_000.0, "M")
} else if abs >= 1_000.0 {
(v / 1_000.0, "K")
} else {
(v, "")
};
format!("{:.prec$}{}", scaled, suffix, prec = prec)
}
pub fn fmt_compact(v: f64) -> String {
fmt_compact_prec(v, 2)
}
pub fn fmt_pnl_compact(abs_usd: f64) -> String {
fmt_compact(abs_usd)
}
pub fn fmt_time_since_secs(elapsed_secs: i64) -> String {
let s = elapsed_secs.max(0);
if s < 60 {
format!("{s}s")
} else if s < 3600 {
format!("{}m", s / 60)
} else if s < 86_400 {
format!("{}h", s / 3600)
} else if s < 604_800 {
format!("{}d", s / 86_400)
} else {
format!("{}w", s / 604_800)
}
}
pub fn pubkey_trader_prefix(trader: &PhoenixPubkey) -> String {
let s = trader.to_string();
s[..4.min(s.len())].to_owned()
}
pub fn pubkey_trader_short(trader: &PhoenixPubkey) -> String {
let s = trader.to_string();
if s.len() <= 8 {
return s;
}
format!("{}\u{2026}{}", &s[..4], &s[s.len() - 4..])
}
pub fn truncate_balance(v: f64) -> f64 {
(v * 100.0).floor() / 100.0
}
pub fn fmt_balance(v: f64) -> String {
format!("{:.2}", truncate_balance(v))
}
pub fn truncate_pubkey(pk: &str) -> String {
if pk.len() <= 8 {
pk.to_string()
} else {
format!("{}...{}", &pk[..4], &pk[pk.len() - 4..])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn balance_formatting() {
assert_eq!(fmt_balance(0.009), "0.00");
assert_eq!(fmt_balance(1.999), "1.99");
assert_eq!(fmt_balance(10.555), "10.55");
}
#[test]
fn pnl_compact_abbreviates_from_thousands() {
assert_eq!(fmt_pnl_compact(999.0), "999.00");
assert_eq!(fmt_pnl_compact(1_000.0), "1.00K");
assert_eq!(fmt_pnl_compact(12_345.67), "12.35K");
assert_eq!(fmt_pnl_compact(1_000_000.0), "1.00M");
assert_eq!(fmt_pnl_compact(1_500_000_000.0), "1.50B");
}
#[test]
fn time_since_picks_largest_fitting_unit() {
assert_eq!(fmt_time_since_secs(0), "0s");
assert_eq!(fmt_time_since_secs(-5), "0s"); assert_eq!(fmt_time_since_secs(59), "59s");
assert_eq!(fmt_time_since_secs(60), "1m");
assert_eq!(fmt_time_since_secs(3599), "59m");
assert_eq!(fmt_time_since_secs(3600), "1h");
assert_eq!(fmt_time_since_secs(86_399), "23h");
assert_eq!(fmt_time_since_secs(86_400), "1d");
assert_eq!(fmt_time_since_secs(604_799), "6d");
assert_eq!(fmt_time_since_secs(604_800), "1w");
assert_eq!(fmt_time_since_secs(2_419_200), "4w");
}
#[test]
fn truncate_pubkey_short_and_long() {
assert_eq!(truncate_pubkey("123"), "123");
assert_eq!(truncate_pubkey("12345678"), "12345678");
assert_eq!(truncate_pubkey("123456789"), "1234...6789");
assert_eq!(truncate_pubkey("SomeLongAddressTokenHere"), "Some...Here");
}
}