switchboard_solana/
decimal.rs1use crate::prelude::*;
3use core::cmp::Ordering;
4use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
5use rust_decimal::Decimal;
6
7#[derive(Default, Eq, PartialEq, Copy, Clone, AnchorSerialize, AnchorDeserialize)]
8pub struct BorshDecimal {
9 pub mantissa: i128,
10 pub scale: u32,
11}
12impl From<Decimal> for BorshDecimal {
13 fn from(s: Decimal) -> Self {
14 Self {
15 mantissa: s.mantissa(),
16 scale: s.scale(),
17 }
18 }
19}
20impl From<&Decimal> for BorshDecimal {
21 fn from(s: &Decimal) -> Self {
22 Self {
23 mantissa: s.mantissa(),
24 scale: s.scale(),
25 }
26 }
27}
28impl From<SwitchboardDecimal> for BorshDecimal {
29 fn from(s: SwitchboardDecimal) -> Self {
30 Self {
31 mantissa: s.mantissa,
32 scale: s.scale,
33 }
34 }
35}
36impl From<BorshDecimal> for SwitchboardDecimal {
37 fn from(val: BorshDecimal) -> Self {
38 SwitchboardDecimal {
39 mantissa: val.mantissa,
40 scale: val.scale,
41 }
42 }
43}
44impl TryInto<Decimal> for &BorshDecimal {
45 type Error = anchor_lang::error::Error;
46 fn try_into(self) -> anchor_lang::Result<Decimal> {
47 Decimal::try_from_i128_with_scale(self.mantissa, self.scale)
48 .map_err(|_| error!(SwitchboardError::DecimalConversionError))
49 }
50}
51
52impl TryInto<Decimal> for BorshDecimal {
53 type Error = anchor_lang::error::Error;
54 fn try_into(self) -> anchor_lang::Result<Decimal> {
55 Decimal::try_from_i128_with_scale(self.mantissa, self.scale)
56 .map_err(|_| error!(SwitchboardError::DecimalConversionError))
57 }
58}
59
60#[zero_copy(unsafe)]
61#[repr(packed)]
62#[derive(Default, Debug, Eq, PartialEq, AnchorDeserialize)]
63pub struct SwitchboardDecimal {
64 pub mantissa: i128,
66 pub scale: u32,
68}
69
70impl SwitchboardDecimal {
71 pub fn new(mantissa: i128, scale: u32) -> SwitchboardDecimal {
72 Self { mantissa, scale }
73 }
74 pub fn from_rust_decimal(d: Decimal) -> SwitchboardDecimal {
75 Self::new(d.mantissa(), d.scale())
76 }
77 pub fn from_f64(v: f64) -> SwitchboardDecimal {
78 let dec = Decimal::from_f64(v).unwrap();
79 Self::from_rust_decimal(dec)
80 }
81 pub fn scale_to(&self, new_scale: u32) -> i128 {
82 match { self.scale }.cmp(&new_scale) {
83 std::cmp::Ordering::Greater => self
84 .mantissa
85 .checked_div(10_i128.pow(self.scale - new_scale))
86 .unwrap(),
87 std::cmp::Ordering::Less => self
88 .mantissa
89 .checked_mul(10_i128.pow(new_scale - self.scale))
90 .unwrap(),
91 std::cmp::Ordering::Equal => self.mantissa,
92 }
93 }
94 pub fn new_with_scale(&self, new_scale: u32) -> Self {
95 let mantissa = self.scale_to(new_scale);
96 SwitchboardDecimal {
97 mantissa,
98 scale: new_scale,
99 }
100 }
101}
102impl From<Decimal> for SwitchboardDecimal {
103 fn from(val: Decimal) -> Self {
104 SwitchboardDecimal::new(val.mantissa(), val.scale())
105 }
106}
107impl TryInto<Decimal> for &SwitchboardDecimal {
108 type Error = anchor_lang::error::Error;
109 fn try_into(self) -> anchor_lang::Result<Decimal> {
110 Decimal::try_from_i128_with_scale(self.mantissa, self.scale)
111 .map_err(|_| error!(SwitchboardError::DecimalConversionError))
112 }
113}
114
115impl TryInto<Decimal> for SwitchboardDecimal {
116 type Error = anchor_lang::error::Error;
117 fn try_into(self) -> anchor_lang::Result<Decimal> {
118 Decimal::try_from_i128_with_scale(self.mantissa, self.scale)
119 .map_err(|_| error!(SwitchboardError::DecimalConversionError))
120 }
121}
122
123impl Ord for SwitchboardDecimal {
124 fn cmp(&self, other: &Self) -> Ordering {
125 let s: Decimal = self.try_into().unwrap();
126 let other: Decimal = other.try_into().unwrap();
127 s.cmp(&other)
128 }
129}
130
131impl PartialOrd for SwitchboardDecimal {
132 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
133 Some(self.cmp(other))
134 }
135 fn lt(&self, other: &Self) -> bool {
136 let s: Decimal = self.try_into().unwrap();
137 let other: Decimal = other.try_into().unwrap();
138 s < other
139 }
140 fn le(&self, other: &Self) -> bool {
141 let s: Decimal = self.try_into().unwrap();
142 let other: Decimal = other.try_into().unwrap();
143 s <= other
144 }
145 fn gt(&self, other: &Self) -> bool {
146 let s: Decimal = self.try_into().unwrap();
147 let other: Decimal = other.try_into().unwrap();
148 s > other
149 }
150 fn ge(&self, other: &Self) -> bool {
151 let s: Decimal = self.try_into().unwrap();
152 let other: Decimal = other.try_into().unwrap();
153 s >= other
154 }
155}
156
157impl From<SwitchboardDecimal> for bool {
158 fn from(s: SwitchboardDecimal) -> Self {
159 let dec: Decimal = (&s).try_into().unwrap();
160 dec.round().mantissa() != 0
161 }
162}
163
164impl TryInto<u64> for SwitchboardDecimal {
165 type Error = anchor_lang::error::Error;
166 fn try_into(self) -> anchor_lang::Result<u64> {
167 let dec: Decimal = (&self).try_into().unwrap();
168 dec.to_u64()
169 .ok_or(error!(SwitchboardError::IntegerOverflowError))
170 }
171}
172
173impl TryInto<i64> for SwitchboardDecimal {
174 type Error = anchor_lang::error::Error;
175 fn try_into(self) -> anchor_lang::Result<i64> {
176 let dec: Decimal = (&self).try_into().unwrap();
177 dec.to_i64()
178 .ok_or(error!(SwitchboardError::IntegerOverflowError))
179 }
180}
181
182impl TryInto<f64> for SwitchboardDecimal {
183 type Error = anchor_lang::error::Error;
184 fn try_into(self) -> anchor_lang::Result<f64> {
185 let dec: Decimal = (&self).try_into().unwrap();
186 dec.to_f64()
187 .ok_or(error!(SwitchboardError::IntegerOverflowError))
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn switchboard_decimal_into_rust_decimal() {
197 let swb_decimal = &SwitchboardDecimal {
198 mantissa: 12345,
199 scale: 2,
200 };
201 let decimal: Decimal = swb_decimal.try_into().unwrap();
202 assert_eq!(decimal.mantissa(), 12345);
203 assert_eq!(decimal.scale(), 2);
204 }
205
206 #[test]
207 fn empty_switchboard_decimal_is_false() {
208 let swb_decimal = SwitchboardDecimal {
209 mantissa: 0,
210 scale: 0,
211 };
212 let b: bool = swb_decimal.into();
213 assert!(!b);
214 let swb_decimal_neg = SwitchboardDecimal {
215 mantissa: -0,
216 scale: 0,
217 };
218 let b: bool = swb_decimal_neg.into();
219 assert!(!b);
220 }
221
222 #[test]
223 fn switchboard_decimal_to_u64() {
224 let swb_decimal = SwitchboardDecimal {
226 mantissa: 12345678,
227 scale: 4,
228 };
229 let b: u64 = swb_decimal.try_into().unwrap();
230 assert_eq!(b, 1234);
231 }
232
233 #[test]
234 fn switchboard_decimal_to_f64() {
235 let swb_decimal = SwitchboardDecimal {
237 mantissa: 12345678,
238 scale: 4,
239 };
240 let b: f64 = swb_decimal.try_into().unwrap();
241 assert_eq!(b, 1234.5678);
242
243 let swb_f64 = SwitchboardDecimal::from_f64(1234.5678);
244 assert_eq!(swb_decimal, swb_f64);
245 }
246}