use df_hyperliquid_core::utils::{Dec, Round};
pub const COLLATERAL_DECIMALS: u32 = 18;
pub const USDC_DECIMALS: u32 = 6;
pub const PAPER_DECIMALS: u32 = 18;
pub const COLLATERAL_PER_USDC: u128 = 1_000_000_000_000;
pub const WAD: u128 = 1_000_000_000_000_000_000;
#[must_use]
pub fn to_dec(raw: u128, decimals: u32) -> Option<Dec> {
Dec::new(i128::try_from(raw).ok()?, decimals)
}
#[must_use]
pub fn from_dec(value: Dec, decimals: u32) -> Option<u128> {
if value.is_negative() {
return None;
}
u128::try_from(value.units_at(decimals, Round::Down)?).ok()
}
#[must_use]
pub fn usd(raw: u128) -> Option<Dec> {
to_dec(raw, COLLATERAL_DECIMALS)
}
#[must_use]
pub fn collateral(dollars: Dec) -> Option<u128> {
from_dec(dollars, COLLATERAL_DECIMALS)
}
#[must_use]
pub const fn usdc_to_collateral(usdc: u64) -> u128 {
usdc as u128 * COLLATERAL_PER_USDC
}
#[must_use]
pub fn collateral_to_usdc(raw: u128) -> Option<u64> {
u64::try_from(raw / COLLATERAL_PER_USDC).ok()
}
#[must_use]
pub fn scale_places(scale: u64) -> Option<u32> {
let mut places = 0;
let mut rest = scale;
while rest > 1 {
if !rest.is_multiple_of(10) {
return None;
}
rest /= 10;
places += 1;
}
(rest == 1).then_some(places)
}
#[must_use]
pub fn price(raw: u128, scale: u64) -> Option<Dec> {
to_dec(raw, scale_places(scale)?)
}
#[must_use]
pub fn price_raw(price: Dec, scale: u64) -> Option<u128> {
let places = scale_places(scale)?;
if price.places() > places {
return None;
}
from_dec(price, places)
}
#[must_use]
pub fn mid(bid: u128, ask: u128) -> Option<u128> {
if bid == 0 || ask == 0 || ask < bid {
return None;
}
Some(bid / 2 + ask / 2 + (bid % 2 + ask % 2) / 2)
}
#[cfg(test)]
#[path = "tests/units.rs"]
mod tests;