levana_perpswap_cosmos/number/
convert.rs1use anyhow::{Context, Result};
2use cosmwasm_std::{Decimal256, Uint128, Uint256};
3
4use super::types::{Signed, UnsignedDecimal};
5use super::Number;
6use std::fmt::Write;
7use std::str::FromStr;
8
9impl Number {
10 pub fn from_ratio_u256<A: Into<Uint256>, B: Into<Uint256>>(
12 numerator: A,
13 denominator: B,
14 ) -> Self {
15 Number::new_positive(Decimal256::from_ratio(numerator, denominator))
16 }
17
18 pub fn to_u128_with_precision(&self, precision: u32) -> Option<u128> {
24 if self.is_negative() {
25 return None;
26 }
27
28 let factor = Decimal256::one().atomics() / Uint256::from_u128(10).pow(precision);
30 let raw = self.value().atomics() / factor;
31
32 Uint128::try_from(raw).ok().map(|x| x.into())
33 }
34
35 pub fn from_fixed_u128(amount: u128, places: u32) -> Self {
38 (Self::from(amount) / Self::from(10u128.pow(places))).unwrap()
39 }
40
41 pub fn to_unsigned_key_bytes(&self) -> Option<[u8; 32]> {
44 if self.is_positive_or_zero() {
45 Some(self.value().atomics().to_be_bytes())
46 } else {
47 None
48 }
49 }
50
51 pub fn from_unsigned_key_bytes(bytes: [u8; 32]) -> Self {
53 Number::new_positive(Decimal256::new(Uint256::from_be_bytes(bytes)))
54 }
55}
56
57impl TryFrom<&str> for Number {
59 type Error = anyhow::Error;
60
61 fn try_from(val: &str) -> Result<Self> {
62 Number::from_str(val)
63 }
64}
65impl TryFrom<String> for Number {
66 type Error = anyhow::Error;
67
68 fn try_from(val: String) -> Result<Self> {
69 Number::from_str(&val)
70 }
71}
72
73impl<T: UnsignedDecimal> FromStr for Signed<T> {
74 type Err = anyhow::Error;
75
76 fn from_str(input: &str) -> Result<Self> {
83 match input.strip_prefix('-') {
84 Some(input) => Decimal256::from_str(input)
85 .map(T::from_decimal256)
86 .map(Signed::new_negative),
87 None => Decimal256::from_str(input)
88 .map(T::from_decimal256)
89 .map(Signed::new_positive),
90 }
91 .with_context(|| format!("Unable to parse Number from {input:?}"))
92 }
93}
94
95impl<T: UnsignedDecimal> From<u128> for Signed<T> {
96 fn from(val: u128) -> Self {
97 Signed::new_positive(T::from_decimal256(Decimal256::from_ratio(val, 1u32)))
98 }
99}
100
101impl<T: UnsignedDecimal> From<u64> for Signed<T> {
102 fn from(val: u64) -> Self {
103 u128::from(val).into()
104 }
105}
106
107#[cfg(test)]
108impl From<f64> for Number {
109 fn from(val: f64) -> Self {
110 Self::from_str(&format!("{}", val)).unwrap()
112 }
113}
114
115impl<T: UnsignedDecimal> std::fmt::Display for Signed<T> {
116 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
117 if self.is_zero() {
118 write!(f, "0")
119 } else {
120 if self.is_negative() {
121 f.write_char('-')?;
122 }
123 write!(f, "{}", self.value())
124 }
125 }
126}
127impl<T: UnsignedDecimal> std::fmt::Debug for Signed<T> {
128 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
129 write!(f, "{}", self)
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn non_ascii_does_not_panic() {
139 Number::try_from("αβ").unwrap_err();
140 }
141
142 #[test]
143 fn roundtrip_unsigned_bytes() {
144 let n = Number::from_str("1.42522").unwrap();
145 let bytes = n.to_unsigned_key_bytes().unwrap();
146 let n2 = Number::from_unsigned_key_bytes(bytes);
147 assert_eq!(n, n2);
148 }
149}