drift_rs/math/
mod.rs

1use crate::{
2    drift_idl::{
3        errors::ErrorCode,
4        types::{MarginCalculationMode, MarginRequirementType, MarketIdentifier},
5    },
6    types::OracleSource,
7};
8
9pub mod account_list_builder;
10pub mod auction;
11pub mod constants;
12pub mod leverage;
13pub mod liquidation;
14pub mod order;
15
16#[derive(Clone, Copy, Debug)]
17pub struct MarginContext {
18    pub margin_type: MarginRequirementType,
19    pub mode: MarginCalculationMode,
20    pub strict: bool,
21    pub margin_buffer: u128,
22    pub fuel_bonus_numerator: i64,
23    pub fuel_bonus: u64,
24    pub fuel_perp_delta: Option<(u16, i64)>,
25    pub fuel_spot_deltas: [(u16, i128); 2],
26}
27
28impl MarginContext {
29    pub fn standard(margin_type: MarginRequirementType) -> Self {
30        Self {
31            margin_type,
32            mode: MarginCalculationMode::Standard {
33                track_open_orders_fraction: false,
34            },
35            strict: false,
36            margin_buffer: 0,
37            fuel_bonus_numerator: 0,
38            fuel_bonus: 0,
39            fuel_perp_delta: None,
40            fuel_spot_deltas: [(0, 0); 2],
41        }
42    }
43
44    pub fn strict(mut self, strict: bool) -> Self {
45        self.strict = strict;
46        self
47    }
48
49    pub fn margin_buffer(mut self, margin_buffer: u32) -> Self {
50        self.margin_buffer = margin_buffer as u128;
51        self
52    }
53
54    // how to change the user's spot position to match how it was prior to instruction change
55    // i.e. diffs are ADDED to perp
56    pub fn fuel_perp_delta(mut self, market_index: u16, delta: i64) -> Self {
57        self.fuel_perp_delta = Some((market_index, delta));
58        self
59    }
60
61    pub fn fuel_spot_delta(mut self, market_index: u16, delta: i128) -> Self {
62        self.fuel_spot_deltas[0] = (market_index, delta);
63        self
64    }
65
66    pub fn fuel_spot_deltas(mut self, deltas: [(u16, i128); 2]) -> Self {
67        self.fuel_spot_deltas = deltas;
68        self
69    }
70
71    pub fn track_open_orders_fraction(mut self) -> Result<Self, ErrorCode> {
72        match self.mode {
73            MarginCalculationMode::Standard {
74                track_open_orders_fraction: ref mut track,
75            } => {
76                *track = true;
77            }
78            _ => {
79                return Err(ErrorCode::InvalidMarginCalculation);
80            }
81        }
82        Ok(self)
83    }
84
85    pub fn liquidation(margin_buffer: u32) -> Self {
86        Self {
87            margin_type: MarginRequirementType::Maintenance,
88            mode: MarginCalculationMode::Liquidation {
89                market_to_track_margin_requirement: None,
90            },
91            margin_buffer: margin_buffer as u128,
92            strict: false,
93            fuel_bonus_numerator: 0,
94            fuel_bonus: 0,
95            fuel_perp_delta: None,
96            fuel_spot_deltas: [(0, 0); 2],
97        }
98    }
99
100    pub fn track_market_margin_requirement(
101        mut self,
102        market_identifier: MarketIdentifier,
103    ) -> Result<Self, ErrorCode> {
104        match self.mode {
105            MarginCalculationMode::Liquidation {
106                market_to_track_margin_requirement: ref mut market_to_track,
107                ..
108            } => {
109                *market_to_track = Some(market_identifier);
110            }
111            _ => {
112                return Err(ErrorCode::InvalidMarginCalculation);
113            }
114        }
115        Ok(self)
116    }
117}
118
119/// Returns (numerator, denominator) pair to normalize prices from 2 different oracle source
120fn get_oracle_normalization_factor(a: OracleSource, b: OracleSource) -> (u64, u64) {
121    match (a, b) {
122        // 1M scaling relationships
123        (OracleSource::PythLazer, OracleSource::PythLazer1M)
124        | (OracleSource::PythPull, OracleSource::Pyth1MPull) => (1_000_000, 1),
125        (OracleSource::PythLazer1M, OracleSource::PythLazer)
126        | (OracleSource::Pyth1MPull, OracleSource::PythPull) => (1, 1_000_000),
127        // 1K scaling relationships
128        (OracleSource::PythLazer, OracleSource::PythLazer1K)
129        | (OracleSource::PythPull, OracleSource::Pyth1KPull) => (1_000, 1),
130        (OracleSource::PythLazer1K, OracleSource::PythLazer)
131        | (OracleSource::Pyth1KPull, OracleSource::PythPull) => (1, 1_000),
132        _ => (1, 1),
133    }
134}