levana_perpswap_cosmos/number/
types.rs1use anyhow::{Context, Result};
6use cosmwasm_std::{Decimal256, OverflowError, Uint128, Uint256};
7use std::{
8 fmt::Display,
9 ops::{Add, Sub},
10 str::FromStr,
11};
12
13#[cfg(feature = "arbitrary")]
15pub fn arbitrary_decimal_256(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Decimal256> {
16 let bytes: [u8; 32] = u.arbitrary()?;
17 let value = Uint256::new(bytes);
18 Ok(Decimal256::new(value))
19}
20
21#[cfg(feature = "arbitrary")]
23pub fn arbitrary_decimal_256_option(
24 u: &mut arbitrary::Unstructured,
25) -> arbitrary::Result<Option<Decimal256>> {
26 let bytes: Option<[u8; 32]> = u.arbitrary()?;
27 Ok(bytes.map(|bytes| {
28 let value = Uint256::new(bytes);
29 Decimal256::new(value)
30 }))
31}
32
33pub trait UnsignedDecimal:
35 Display
36 + std::fmt::Debug
37 + serde::Serialize
38 + serde::de::DeserializeOwned
39 + Copy
40 + Ord
41 + FromStr
42 + Default
43{
44 fn into_decimal256(self) -> Decimal256;
46
47 fn from_decimal256(src: Decimal256) -> Self;
49
50 fn is_zero(&self) -> bool {
52 self.into_decimal256().is_zero()
53 }
54
55 fn checked_add(self, rhs: Self) -> Result<Self, OverflowError> {
57 self.into_decimal256()
58 .checked_add(rhs.into_decimal256())
59 .map(Self::from_decimal256)
60 }
61
62 fn checked_add_signed(self, rhs: Signed<Self>) -> Result<Self> {
64 self.into_signed()
65 .checked_add(rhs)?
66 .try_into_non_negative_value()
67 .with_context(|| format!("{self} + {rhs}"))
68 }
69
70 fn checked_sub(self, rhs: Self) -> Result<Self, OverflowError> {
72 self.into_decimal256()
73 .checked_sub(rhs.into_decimal256())
74 .map(Self::from_decimal256)
75 }
76
77 fn try_from_number(Signed { value, negative }: Signed<Decimal256>) -> anyhow::Result<Self> {
79 if negative {
80 Err(anyhow::anyhow!(
81 "try_from_number: received a negative value"
82 ))
83 } else {
84 Ok(Self::from_decimal256(value))
85 }
86 }
87
88 fn into_number(self) -> Signed<Decimal256> {
90 Signed::new_positive(self.into_decimal256())
91 }
92
93 fn into_signed(self) -> Signed<Self> {
95 Signed::new_positive(self)
96 }
97
98 fn zero() -> Self {
100 Self::from_decimal256(Decimal256::zero())
101 }
102
103 fn two() -> Self {
105 Self::from_decimal256(Decimal256::from_atomics(2u128, 0).unwrap())
106 }
107
108 fn diff(self, rhs: Self) -> Self {
110 Self::from_decimal256(if self > rhs {
111 self.into_decimal256() - rhs.into_decimal256()
112 } else {
113 rhs.into_decimal256() - self.into_decimal256()
114 })
115 }
116
117 fn approx_eq(self, rhs: Self) -> bool {
121 self.diff(rhs).into_decimal256() < Decimal256::from_ratio(1u32, 10_000_000u32)
122 }
123
124 }
127
128impl UnsignedDecimal for Decimal256 {
129 fn into_decimal256(self) -> Decimal256 {
130 self
131 }
132
133 fn from_decimal256(src: Decimal256) -> Self {
134 src
135 }
136}
137
138macro_rules! unsigned {
139 ($t:tt) => {
140 #[derive(
142 PartialEq,
143 Eq,
144 PartialOrd,
145 Ord,
146 Clone,
147 Copy,
148 Default,
149 serde::Serialize,
150 serde::Deserialize,
151 schemars::JsonSchema,
152 )]
153 pub struct $t(Decimal256);
155
156 impl $t {
157 pub const fn zero() -> Self {
159 Self(Decimal256::zero())
160 }
161
162 pub const fn one() -> Self {
164 Self(Decimal256::one())
165 }
166 }
167
168 impl UnsignedDecimal for $t {
169 fn into_decimal256(self) -> Decimal256 {
170 self.0
171 }
172
173 fn from_decimal256(src: Decimal256) -> Self {
174 Self(src)
175 }
176 }
177
178 impl Display for $t {
179 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
180 write!(f, "{}", self.0)
181 }
182 }
183
184 impl std::fmt::Debug for $t {
185 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
186 write!(f, "{}({})", stringify!($t), self.0)
187 }
188 }
189
190 impl FromStr for $t {
191 type Err = anyhow::Error;
192
193 fn from_str(s: &str) -> Result<Self> {
194 parse_decimal256(s).map(Self::from_decimal256)
195 }
196 }
197
198 impl TryFrom<&str> for $t {
199 type Error = anyhow::Error;
200
201 fn try_from(value: &str) -> Result<Self> {
202 value.parse()
203 }
204 }
205
206 impl TryFrom<String> for Signed<$t> {
207 type Error = anyhow::Error;
208
209 fn try_from(value: String) -> Result<Self> {
210 value.parse()
211 }
212 }
213
214 impl TryFrom<&str> for Signed<$t> {
215 type Error = anyhow::Error;
216
217 fn try_from(value: &str) -> Result<Self> {
218 value.parse()
219 }
220 }
221
222 impl TryFrom<String> for $t {
223 type Error = anyhow::Error;
224
225 fn try_from(value: String) -> Result<Self> {
226 value.parse()
227 }
228 }
229
230 impl Add for $t {
231 type Output = anyhow::Result<Self, OverflowError>;
232
233 fn add(self, rhs: Self) -> Self::Output {
234 Ok(Self(self.0.checked_add(rhs.0)?))
235 }
236 }
237
238 impl Sub for $t {
239 type Output = anyhow::Result<Self, OverflowError>;
240
241 fn sub(self, rhs: Self) -> Self::Output {
242 Ok(Self(self.0.checked_sub(rhs.0)?))
243 }
244 }
245
246 impl From<u64> for $t {
247 fn from(src: u64) -> Self {
248 u128::from(src).into()
249 }
250 }
251
252 impl From<u128> for $t {
253 fn from(src: u128) -> Self {
254 Self::from_decimal256(Decimal256::from_ratio(src, 1u32))
255 }
256 }
257
258 #[cfg(feature = "arbitrary")]
259 impl<'a> arbitrary::Arbitrary<'a> for $t {
260 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
261 Ok(Self::from_decimal256(arbitrary_decimal_256(u)?))
262 }
263 }
264
265 impl $t {
266 pub fn floor_with_precision(&self, precision: u32) -> Self {
268 let factor = Decimal256::one().atomics() / Uint256::from_u128(10).pow(precision);
270 let raw = self.0.atomics() / factor * factor;
271
272 Self(Decimal256::new(raw))
273 }
274 }
275 };
276}
277
278fn parse_decimal256(s: &str) -> Result<Decimal256> {
279 s.parse()
280 .with_context(|| format!("Unable to parse unsigned decimal from {s}"))
281}
282
283unsigned!(Collateral);
284unsigned!(Notional);
285unsigned!(Base);
286unsigned!(Quote);
287unsigned!(Usd);
288unsigned!(LpToken);
289unsigned!(FarmingToken);
290unsigned!(LvnToken);
291unsigned!(LockdropShares);
292
293#[derive(PartialEq, Eq, Clone, Copy)]
295pub struct Signed<T> {
296 value: T,
297 negative: bool,
299}
300
301impl<T: UnsignedDecimal> Default for Signed<T> {
302 fn default() -> Self {
303 Signed {
304 value: T::default(),
305 negative: false,
306 }
307 }
308}
309
310impl<T> From<T> for Signed<T> {
311 fn from(value: T) -> Self {
312 Signed {
313 value,
314 negative: false,
315 }
316 }
317}
318
319impl<T: UnsignedDecimal> Signed<T> {
320 pub(crate) fn value(self) -> T {
321 self.value
322 }
323
324 pub fn is_negative(&self) -> bool {
326 self.negative
327 }
328
329 pub(crate) fn new_positive(value: T) -> Self {
331 Self {
332 value,
333 negative: false,
334 }
335 }
336
337 pub(crate) fn new_negative(value: T) -> Self {
339 Self {
340 value,
341 negative: !value.is_zero(),
342 }
343 }
344
345 pub fn into_number(self) -> Signed<Decimal256> {
347 Signed {
348 value: self.value.into_decimal256(),
349 negative: self.negative,
350 }
351 }
352
353 pub fn from_number(src: Signed<Decimal256>) -> Self {
355 Signed {
356 value: T::from_decimal256(src.value),
357 negative: src.negative,
358 }
359 }
360
361 pub fn zero() -> Self {
363 Signed::new_positive(T::zero())
364 }
365
366 pub fn two() -> Self {
368 Signed::new_positive(T::two())
369 }
370
371 pub fn try_into_non_negative_value(self) -> Option<T> {
373 if self.is_negative() {
374 None
375 } else {
376 Some(self.value())
377 }
378 }
379
380 pub fn try_into_non_zero(self) -> Option<NonZero<T>> {
382 self.try_into_non_negative_value().and_then(NonZero::new)
383 }
384}
385
386impl Signed<Decimal256> {
387 pub const MAX: Self = Self {
389 value: Decimal256::MAX,
390 negative: false,
391 };
392
393 pub const MIN: Number = Number {
395 value: Decimal256::MAX,
396 negative: true,
397 };
398
399 pub const ONE: Number = Number {
401 value: Decimal256::one(),
402 negative: false,
403 };
404
405 pub const NEG_ONE: Number = Number {
407 value: Decimal256::one(),
408 negative: true,
409 };
410
411 pub const ZERO: Number = Number {
413 value: Decimal256::zero(),
414 negative: false,
415 };
416
417 pub const EPS_E7: Number = Number {
422 value: Decimal256::raw(100_000_000_000),
424 negative: false,
425 };
426
427 pub const EPS_E6: Number = Number {
429 value: Decimal256::raw(1_000_000_000_000),
430 negative: false,
431 };
432
433 pub const EPS_E17: Number = Number {
437 value: Decimal256::raw(10),
439 negative: false,
440 };
441}
442
443impl<T: UnsignedDecimal> std::ops::Neg for Signed<T> {
444 type Output = Self;
445
446 fn neg(mut self) -> Self {
447 if !self.value.is_zero() {
448 self.negative = !self.negative;
449 }
450 self
451 }
452}
453
454#[cfg(feature = "arbitrary")]
455impl<'a> arbitrary::Arbitrary<'a> for Signed<Decimal256> {
456 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
457 Ok(Self {
458 value: arbitrary_decimal_256(u)?,
459 negative: u.arbitrary()?,
460 })
461 }
462}
463
464#[cfg(feature = "arbitrary")]
465impl<'a> arbitrary::Arbitrary<'a> for Signed<Collateral> {
466 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
467 Ok(Self {
468 value: u.arbitrary()?,
469 negative: u.arbitrary()?,
470 })
471 }
472}
473
474#[cfg(feature = "arbitrary")]
475impl<'a> arbitrary::Arbitrary<'a> for Signed<Notional> {
476 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
477 Ok(Self {
478 value: u.arbitrary()?,
479 negative: u.arbitrary()?,
480 })
481 }
482}
483
484pub type Number = Signed<Decimal256>;
493
494#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Debug)]
496pub struct NonZero<T>(T);
497
498#[cfg(feature = "arbitrary")]
499impl<'a> arbitrary::Arbitrary<'a> for NonZero<Decimal256> {
500 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
501 let v = arbitrary_decimal_256(u)?;
502 if v.is_zero() {
503 Ok(Self(Decimal256::one()))
504 } else {
505 Ok(Self(v))
506 }
507 }
508}
509#[cfg(feature = "arbitrary")]
510impl<'a> arbitrary::Arbitrary<'a> for NonZero<LpToken> {
511 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
512 NonZero::<Decimal256>::arbitrary(u).map(|v| Self(LpToken::from_decimal256(v.0)))
513 }
514}
515#[cfg(feature = "arbitrary")]
516impl<'a> arbitrary::Arbitrary<'a> for NonZero<Collateral> {
517 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
518 NonZero::<Decimal256>::arbitrary(u).map(|v| Self(Collateral::from_decimal256(v.0)))
519 }
520}
521
522pub type NumberGtZero = NonZero<Decimal256>;
530
531impl<T: UnsignedDecimal> NonZero<T> {
532 pub fn to_be_bytes(&self) -> [u8; 32] {
534 self.0.into_decimal256().atomics().to_be_bytes()
535 }
536
537 pub fn from_be_bytes(bytes: [u8; 32]) -> Option<Self> {
541 if bytes == [0; 32] {
542 None
543 } else {
544 Some(NonZero(T::from_decimal256(Decimal256::new(
545 Uint256::from_be_bytes(bytes),
546 ))))
547 }
548 }
549
550 pub fn raw(self) -> T {
552 self.0
553 }
554
555 pub fn into_signed(self) -> Signed<T> {
557 self.0.into()
558 }
559
560 pub fn new(src: T) -> Option<Self> {
562 if src.is_zero() {
563 None
564 } else {
565 Some(NonZero(src))
566 }
567 }
568
569 pub fn into_decimal256(self) -> Decimal256 {
571 self.0.into_decimal256()
572 }
573
574 pub fn into_number_gt_zero(self) -> NumberGtZero {
576 NonZero::<Decimal256>(self.into_decimal256())
577 }
578
579 pub fn into_number(self) -> Signed<Decimal256> {
581 self.0.into_number()
582 }
583
584 pub fn try_from_number(src: Signed<Decimal256>) -> Option<Self> {
586 T::try_from_number(src).ok().and_then(NonZero::new)
587 }
588
589 pub fn try_from_decimal(src: Decimal256) -> Option<Self> {
591 NonZero::new(T::from_decimal256(src))
592 }
593
594 pub fn try_from_signed(src: Signed<T>) -> Result<Self> {
596 src.try_into_non_negative_value()
597 .and_then(NonZero::new)
598 .with_context(|| format!("Could not converted signed value {src} into NonZero"))
599 }
600
601 pub fn checked_add(self, rhs: T) -> Result<Self> {
606 self.raw()
607 .checked_add(rhs)
608 .context("NonZero::checked_add overflow")
609 .map(|x| NonZero::new(x).expect("Impossible! NonZero::checked_add returned 0"))
610 }
611
612 pub fn checked_sub(self, rhs: T) -> Result<Self> {
616 self.raw()
617 .checked_sub(rhs)
618 .ok()
619 .and_then(NonZero::new)
620 .with_context(|| format!("NonZero::checked_sub: cannot perform {self} - {rhs}"))
621 }
622
623 pub fn checked_add_signed(self, rhs: Signed<T>) -> Result<Self> {
626 NonZero::try_from_signed(self.into_signed().checked_add(rhs)?)
627 .with_context(|| format!("{self} + {rhs}"))
628 }
629
630 pub fn checked_sub_signed(self, rhs: Signed<T>) -> Result<Self> {
633 NonZero::try_from_signed(self.into_signed().checked_add(-rhs)?)
634 .with_context(|| format!("{self} - {rhs}"))
635 }
636
637 pub fn one() -> Self {
639 Self(T::from_decimal256(Decimal256::one()))
640 }
641}
642
643impl<T: UnsignedDecimal> From<NonZero<T>> for Signed<T> {
644 fn from(src: NonZero<T>) -> Self {
645 Signed::new_positive(src.0)
646 }
647}
648
649impl<T: UnsignedDecimal> TryFrom<Signed<T>> for NonZero<T> {
650 type Error = anyhow::Error;
651
652 fn try_from(value: Signed<T>) -> Result<Self, Self::Error> {
653 if value.is_strictly_positive() {
654 Ok(NonZero(value.value()))
655 } else {
656 Err(anyhow::anyhow!(
657 "Cannot convert Signed to NonZero, value is {value}"
658 ))
659 }
660 }
661}
662
663impl Collateral {
664 pub fn checked_mul_dec(self, rhs: Decimal256) -> Result<Collateral> {
666 self.0
667 .checked_mul(rhs)
668 .map(Collateral)
669 .with_context(|| format!("Collateral::checked_mul_dec failed on {self} * {rhs}"))
670 }
671
672 pub fn checked_div_dec(self, rhs: Decimal256) -> Result<Collateral> {
674 self.0
675 .checked_div(rhs)
676 .map(Collateral)
677 .with_context(|| format!("Collateral::checked_div_dec failed on {self} * {rhs}"))
678 }
679
680 pub fn div_non_zero_dec(self, rhs: NonZero<Decimal256>) -> Collateral {
682 Collateral::from_decimal256(self.into_decimal256() / rhs.into_decimal256())
683 }
684
685 pub fn div_non_zero(self, rhs: NonZero<Collateral>) -> Decimal256 {
687 self.into_decimal256() / rhs.into_decimal256()
688 }
689}
690
691impl Usd {
692 pub fn checked_mul_dec(self, rhs: Decimal256) -> Result<Usd> {
694 self.0
695 .checked_mul(rhs)
696 .map(Usd)
697 .with_context(|| format!("Usd::checked_mul_ratio failed on {self} * {rhs}"))
698 }
699}
700
701impl NonZero<Collateral> {
702 pub fn checked_mul_non_zero(self, rhs: NonZero<Decimal256>) -> Result<NonZero<Collateral>> {
704 self.0.checked_mul_dec(rhs.raw()).map(|x| {
705 debug_assert!(!x.is_zero());
706 NonZero(x)
707 })
708 }
709
710 pub fn checked_div_collateral(self, rhs: NonZero<Collateral>) -> Result<NonZero<Decimal256>> {
714 Ok(NonZero(
715 self.into_decimal256().checked_div(rhs.into_decimal256())?,
716 ))
717 }
718}
719
720impl<T: UnsignedDecimal> Signed<T> {
721 pub fn checked_mul_number(self, rhs: Signed<Decimal256>) -> Result<Self> {
723 self.into_number().checked_mul(rhs).map(Self::from_number)
724 }
725}
726
727const LP_TOKEN_DIVIDER: u64 = 1_000_000_000_000;
732
733impl LpToken {
734 pub const PRECISION: u8 = 6;
736
737 pub fn into_u128(self) -> Result<u128> {
741 Ok(Uint128::try_from(
742 self.into_decimal256()
743 .atomics()
744 .checked_div(LP_TOKEN_DIVIDER.into())?,
745 )?
746 .u128())
747 }
748
749 pub fn from_u128(x: u128) -> Result<Self> {
751 Ok(LpToken::from_decimal256(Decimal256::from_atomics(
752 x,
753 Self::PRECISION.into(),
754 )?))
755 }
756}
757
758impl LvnToken {
759 pub fn checked_mul_dec(self, rhs: Decimal256) -> Result<LvnToken> {
761 self.0
762 .checked_mul(rhs)
763 .map(LvnToken)
764 .with_context(|| format!("LvnToken::checked_mul failed on {self} * {rhs}"))
765 }
766
767 pub fn checked_div_dec(self, rhs: Decimal256) -> Result<LvnToken> {
769 self.0
770 .checked_div(rhs)
771 .map(LvnToken)
772 .with_context(|| format!("LvnToken::checked_div failed on {self} / {rhs}"))
773 }
774}
775
776#[cfg(test)]
777mod tests {
778 use super::*;
779
780 #[test]
781 fn lp_token_u128_roundtrip() {
782 assert_eq!(
783 LpToken::from_str("12.3456789")
784 .unwrap()
785 .into_u128()
786 .unwrap(),
787 12345678
788 );
789 assert_eq!(
790 LpToken::from_str("12.345678").unwrap(),
791 LpToken::from_u128(12345678).unwrap(),
792 );
793 }
794
795 #[test]
796 fn floor_unsigned_type_with_precision() {
797 unsigned!(UnsignedStruct);
798
799 assert_eq!(
800 UnsignedStruct::from_str("12.3456789")
801 .unwrap()
802 .floor_with_precision(2),
803 UnsignedStruct::from_str("12.34").unwrap()
804 );
805 }
806}