// file: crates/ksp-app-wallet-desk/src/wallet_balance.rs // version: 4 //! Safe Wallet balance projection produced by the typed HTTP Transport path. use ts_rs::TS; // rust-rules: trait-import /// Safe balance snapshot for the currently authorized Wallet. #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_balance/WalletBalanceDto.ts")] pub(crate) struct WalletBalanceDto { /// Optional Solana RPC API version reported by the selected node. pub(crate) api_version: std::option::Option, /// Exact decimal account balance in lamports, encoded as text to avoid JavaScript integer precision loss. pub(crate) lamports: String, /// RPC context slot associated with this balance. pub(crate) slot: u64, /// Exact decimal SOL presentation derived from lamports without floating point. pub(crate) sol: String, /// Arithmetic mean of successful SOL/USD observations produced by this refresh, or unavailable when no provider returned one. pub(crate) sol_usd_average: std::option::Option, /// Exact USD equivalent of the current SOL balance using `sol_usd_average`, or unavailable when the auxiliary price is unavailable. pub(crate) sol_usd_equivalent: std::option::Option, /// Composite-selected Transport profile used for the RPC. pub(crate) transport_profile: String, /// Logical Transport role used for the RPC. pub(crate) transport_role: String, /// Root-scoped Wallet identifier whose authorized Pubkey was queried. pub(crate) wallet_id: String, } /// Formats exact lamports as a decimal SOL string with nine fractional digits. #[must_use] pub(crate) fn format_lamports_as_sol(lamports: u64) -> String { let whole = lamports / 1_000_000_000; let fractional = lamports % 1_000_000_000; return format!("{whole}.{fractional:09}"); } /// Computes a deterministic SOL/USD arithmetic mean without IEEE-754 conversion. /// /// The output is rounded half-up to at most eight fractional digits and trailing zeroes are removed. Arithmetic overflow degrades to `None`, which Wallet Desk /// renders as `N.A.` without affecting the authoritative SOL balance. #[must_use] pub(crate) fn average_sol_usd_prices(prices: &[ksp_offchain_transport_lib::MarketPriceDecimal]) -> std::option::Option { const OUTPUT_SCALE: u8 = 8; if prices.is_empty() { return std::option::Option::None; } let max_scale = prices.iter().map(ksp_offchain_transport_lib::MarketPriceDecimal::scale).max(); let max_scale = match max_scale { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let mut sum = 0_u128; for price in prices { let exponent = u32::from(max_scale.saturating_sub(price.scale())); let factor = 10_u128.checked_pow(exponent); let factor = match factor { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let coefficient = price.coefficient().checked_mul(factor); let coefficient = match coefficient { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; sum = match sum.checked_add(coefficient) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; } let count = match u128::try_from(prices.len()) { std::result::Result::Ok(value) if value > 0 => value, _ => return std::option::Option::None, }; let target_scale = OUTPUT_SCALE; let (numerator, denominator) = if max_scale <= target_scale { let factor = 10_u128.checked_pow(u32::from(target_scale - max_scale)); let factor = match factor { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let numerator = match sum.checked_mul(factor) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; (numerator, count) } else { let factor = 10_u128.checked_pow(u32::from(max_scale - target_scale)); let factor = match factor { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let denominator = match count.checked_mul(factor) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; (sum, denominator) }; let quotient = numerator / denominator; let remainder = numerator % denominator; let round_up = remainder >= denominator.div_ceil(2); let coefficient = if round_up { match quotient.checked_add(1) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, } } else { quotient }; return std::option::Option::Some(format_scaled_decimal(coefficient, target_scale)); } /// Computes the exact USD equivalent of a lamport balance from the current SOL/USD average. /// /// The output is rounded half-up to at most eight fractional digits. Missing or invalid auxiliary price data and arithmetic overflow degrade to `None`; the /// authoritative SOL balance remains unaffected. #[must_use] pub(crate) fn sol_usd_equivalent(lamports: u64, sol_usd_average: std::option::Option<&str>) -> std::option::Option { const LAMPORT_SCALE: u8 = 9; const OUTPUT_SCALE: u8 = 8; let source = match sol_usd_average { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let average = match ksp_offchain_transport_lib::MarketPriceDecimal::parse(source) { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return std::option::Option::None, }; let coefficient = match u128::from(lamports).checked_mul(average.coefficient()) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let source_scale = u16::from(LAMPORT_SCALE) + u16::from(average.scale()); let target_scale = u16::from(OUTPUT_SCALE); let rounded_coefficient = if source_scale <= target_scale { let exponent = u32::from(target_scale - source_scale); let factor = match 10_u128.checked_pow(exponent) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; match coefficient.checked_mul(factor) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, } } else { let exponent = u32::from(source_scale - target_scale); let divisor = match 10_u128.checked_pow(exponent) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let quotient = coefficient / divisor; let remainder = coefficient % divisor; if remainder >= divisor.div_ceil(2) { match quotient.checked_add(1) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, } } else { quotient } }; return std::option::Option::Some(format_scaled_decimal(rounded_coefficient, OUTPUT_SCALE)); } fn format_scaled_decimal(mut coefficient: u128, mut scale: u8) -> String { while scale > 0 && coefficient.is_multiple_of(10) { coefficient /= 10; scale -= 1; } let digits = coefficient.to_string(); if scale == 0 { return digits; } let scale = usize::from(scale); if digits.len() > scale { let split = digits.len() - scale; return format!("{}.{}", &digits[..split], &digits[split..]); } return format!("0.{}{}", "0".repeat(scale - digits.len()), digits); } #[cfg(test)] #[path = "../unit_tests/wallet_balance.rs"] mod tests;