phoenix/
quantities.rs

1//! We define wrapper types around primitive number types to ensure that we
2//! only do arithmetic on quantities that make sense.
3
4// By aliasing the BorshDeserialize and BorshSerialize traits, we prevent Shank from
5// writing structs with these annotations to the IDL.
6use borsh::{BorshDeserialize as Deserialize, BorshSerialize as Serialize};
7use bytemuck::{Pod, Zeroable};
8use std::fmt::Display;
9use std::iter::Sum;
10use std::ops::{Add, AddAssign, Div, Mul, Rem, Sub, SubAssign};
11
12pub trait WrapperU64 {
13    fn new(value: u64) -> Self;
14    fn as_u64(&self) -> u64;
15}
16
17macro_rules! basic_u64_struct {
18    ($type_name:ident) => {
19        #[derive(Debug, Clone, Copy, PartialOrd, Ord, Zeroable, Pod)]
20        #[repr(transparent)]
21        pub struct $type_name {
22            inner: u64,
23        }
24
25        basic_u64!($type_name);
26    };
27}
28
29macro_rules! basic_u64 {
30    ($type_name:ident) => {
31        impl WrapperU64 for $type_name {
32            fn new(value: u64) -> Self {
33                $type_name { inner: value }
34            }
35
36            fn as_u64(&self) -> u64 {
37                self.inner
38            }
39        }
40
41        impl $type_name {
42            pub const ZERO: Self = $type_name { inner: 0 };
43            pub const ONE: Self = $type_name { inner: 1 };
44            pub const MAX: Self = $type_name { inner: u64::MAX };
45            pub const MIN: Self = $type_name { inner: u64::MIN };
46            pub fn as_u128(&self) -> u128 {
47                self.inner as u128
48            }
49
50            pub fn saturating_sub(self, other: Self) -> Self {
51                $type_name::new(self.inner.saturating_sub(other.inner))
52            }
53
54            pub fn unchecked_div<Divisor: WrapperU64, Quotient: WrapperU64>(
55                self,
56                other: Divisor,
57            ) -> Quotient {
58                Quotient::new(self.inner / other.as_u64())
59            }
60        }
61
62        impl Display for $type_name {
63            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
64                self.inner.fmt(f)
65            }
66        }
67
68        impl Mul for $type_name {
69            type Output = Self;
70            fn mul(self, other: Self) -> Self {
71                $type_name::new(self.inner * other.inner)
72            }
73        }
74
75        impl Sum<$type_name> for $type_name {
76            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
77                iter.fold($type_name::ZERO, |acc, x| acc + x)
78            }
79        }
80
81        impl Add for $type_name {
82            type Output = Self;
83            fn add(self, other: Self) -> Self {
84                $type_name::new(self.inner + other.inner)
85            }
86        }
87
88        impl AddAssign for $type_name {
89            fn add_assign(&mut self, other: Self) {
90                *self = *self + other;
91            }
92        }
93
94        impl Sub for $type_name {
95            type Output = Self;
96
97            fn sub(self, other: Self) -> Self {
98                $type_name::new(self.inner - other.inner)
99            }
100        }
101
102        impl SubAssign for $type_name {
103            fn sub_assign(&mut self, other: Self) {
104                *self = *self - other;
105            }
106        }
107
108        impl Default for $type_name {
109            fn default() -> Self {
110                Self::ZERO
111            }
112        }
113
114        impl PartialEq for $type_name {
115            fn eq(&self, other: &Self) -> bool {
116                self.inner == other.inner
117            }
118        }
119
120        impl From<$type_name> for u64 {
121            fn from(x: $type_name) -> u64 {
122                x.inner
123            }
124        }
125
126        impl From<$type_name> for f64 {
127            fn from(x: $type_name) -> f64 {
128                x.inner as f64
129            }
130        }
131
132        impl Eq for $type_name {}
133
134        // Below should only be used in tests.
135        impl PartialEq<u64> for $type_name {
136            fn eq(&self, other: &u64) -> bool {
137                self.inner == *other
138            }
139        }
140
141        impl PartialEq<$type_name> for u64 {
142            fn eq(&self, other: &$type_name) -> bool {
143                *self == other.inner
144            }
145        }
146    };
147}
148
149macro_rules! allow_multiply {
150    ($type_1:ident, $type_2:ident, $type_result:ident) => {
151        impl Mul<$type_2> for $type_1 {
152            type Output = $type_result;
153            fn mul(self, other: $type_2) -> $type_result {
154                $type_result::new(self.inner * other.inner)
155            }
156        }
157
158        impl Mul<$type_1> for $type_2 {
159            type Output = $type_result;
160            fn mul(self, other: $type_1) -> $type_result {
161                $type_result::new(self.inner * other.inner)
162            }
163        }
164
165        impl Div<$type_1> for $type_result {
166            type Output = $type_2;
167            #[track_caller]
168            fn div(self, other: $type_1) -> $type_2 {
169                if self.inner % other.inner != 0 {
170                    let caller = std::panic::Location::caller();
171
172                    phoenix_log!(
173                        "WARNING: Expected clean division, but received {:?} / {:?}. Caller: {:?}",
174                        self,
175                        other,
176                        caller
177                    );
178                }
179                $type_2::new(self.inner / other.inner)
180            }
181        }
182
183        impl Div<$type_2> for $type_result {
184            type Output = $type_1;
185            #[track_caller]
186            fn div(self, other: $type_2) -> $type_1 {
187                if self.inner % other.inner != 0 {
188                    let caller = std::panic::Location::caller();
189
190                    phoenix_log!(
191                        "WARNING: Expected clean division, but received {:?} / {:?}. Caller: {:?}",
192                        self,
193                        other,
194                        caller
195                    );
196                }
197                $type_1::new(self.inner / other.inner)
198            }
199        }
200    };
201}
202
203macro_rules! allow_mod {
204    ($type_1:ident, $type_2:ident) => {
205        impl Rem<$type_2> for $type_1 {
206            type Output = u64;
207            fn rem(self, other: $type_2) -> u64 {
208                self.inner % other.inner
209            }
210        }
211    };
212}
213
214// These structs need to be explicitly defined outside of the macro generation because the
215// OrderPacket type (which contains these units) implements BorshSerialize and BorshDeserialize
216#[derive(Debug, Clone, Copy, PartialOrd, Ord, Zeroable, Pod, Deserialize, Serialize)]
217#[repr(transparent)]
218pub struct QuoteLots {
219    inner: u64,
220}
221#[derive(Debug, Clone, Copy, PartialOrd, Ord, Zeroable, Pod, Deserialize, Serialize)]
222#[repr(transparent)]
223pub struct BaseLots {
224    inner: u64,
225}
226
227#[derive(Debug, Clone, Copy, PartialOrd, Ord, Zeroable, Pod, Deserialize, Serialize)]
228#[repr(transparent)]
229pub struct Ticks {
230    inner: u64,
231}
232
233basic_u64!(QuoteLots);
234basic_u64!(BaseLots);
235
236// Discrete price unit (quote quantity per base quantity)
237basic_u64!(Ticks);
238
239// Quantities
240basic_u64_struct!(QuoteAtoms);
241basic_u64_struct!(BaseAtoms);
242basic_u64_struct!(QuoteUnits);
243basic_u64_struct!(BaseUnits);
244
245// Dimensionless conversion factors
246basic_u64_struct!(QuoteAtomsPerQuoteLot);
247basic_u64_struct!(BaseAtomsPerBaseLot);
248basic_u64_struct!(BaseLotsPerBaseUnit);
249basic_u64_struct!(QuoteLotsPerQuoteUnit);
250basic_u64_struct!(QuoteAtomsPerQuoteUnit);
251basic_u64_struct!(BaseAtomsPerBaseUnit);
252
253// Dimensionless tick sizes
254basic_u64_struct!(QuoteAtomsPerBaseUnitPerTick);
255basic_u64_struct!(QuoteLotsPerBaseUnitPerTick);
256
257basic_u64_struct!(AdjustedQuoteLots);
258basic_u64_struct!(QuoteLotsPerBaseUnit);
259
260// Conversions from units to lots
261allow_multiply!(BaseUnits, BaseLotsPerBaseUnit, BaseLots);
262allow_multiply!(QuoteUnits, QuoteLotsPerQuoteUnit, QuoteLots);
263// Conversions from lots to atoms
264allow_multiply!(QuoteLots, QuoteAtomsPerQuoteLot, QuoteAtoms);
265allow_multiply!(BaseLots, BaseAtomsPerBaseLot, BaseAtoms);
266
267// Conversion from atoms per lot to units
268allow_multiply!(
269    BaseAtomsPerBaseLot,
270    BaseLotsPerBaseUnit,
271    BaseAtomsPerBaseUnit
272);
273allow_multiply!(
274    QuoteAtomsPerQuoteLot,
275    QuoteLotsPerQuoteUnit,
276    QuoteAtomsPerQuoteUnit
277);
278
279// Conversion between units of tick size
280allow_multiply!(
281    QuoteLotsPerBaseUnitPerTick,
282    QuoteAtomsPerQuoteLot,
283    QuoteAtomsPerBaseUnitPerTick
284);
285
286// Conversion from ticks to price
287allow_multiply!(QuoteLotsPerBaseUnitPerTick, Ticks, QuoteLotsPerBaseUnit);
288
289// Conversion from quote lots to adjusted quote lots
290allow_multiply!(QuoteLots, BaseLotsPerBaseUnit, AdjustedQuoteLots);
291
292// Intermediate conversions for extracting quote lots from book orders
293allow_multiply!(QuoteLotsPerBaseUnit, BaseLots, AdjustedQuoteLots);
294
295allow_mod!(AdjustedQuoteLots, BaseLotsPerBaseUnit);
296allow_mod!(BaseAtomsPerBaseUnit, BaseLotsPerBaseUnit);
297allow_mod!(QuoteAtomsPerQuoteUnit, QuoteLotsPerQuoteUnit);
298allow_mod!(QuoteLotsPerBaseUnitPerTick, BaseLotsPerBaseUnit);
299
300#[test]
301fn test_new_constructor_macro() {
302    let base_lots_1 = BaseLots::new(5);
303    let base_lots_2 = BaseLots::new(10);
304
305    assert_eq!(base_lots_1 + base_lots_2, BaseLots::new(15));
306
307    // Below code (correctly) fails to compile.
308    // let quote_lots_1 = QuoteLots::new(5);
309    // let result = quote_lots_1 + base_lots_1;
310}
311
312#[test]
313fn test_multiply_macro() {
314    let base_units = BaseUnits::new(5);
315    let base_lots_per_base_unit = BaseLotsPerBaseUnit::new(100);
316    assert_eq!(base_units * base_lots_per_base_unit, BaseLots::new(500));
317
318    // Below code (correctly) fails to compile.
319    // let quote_units = QuoteUnits::new(5);
320    // let result = quote_units * base_lots_per_base_unit;
321}