Skip to main content

lb_clmm_fork/state/
parameters.rs

1use crate::constants::{BASIS_POINT_MAX, MAX_BASE_FACTOR_STEP, MAX_PROTOCOL_SHARE};
2use crate::instructions::update_fee_parameters::FeeParameter;
3use crate::{errors::LBError, math::safe_math::SafeMath};
4use anchor_lang::prelude::*;
5
6#[zero_copy]
7#[derive(InitSpace, Debug)]
8/// Parameter that set by the protocol
9pub struct StaticParameters {
10    /// Used for base fee calculation. base_fee_rate = base_factor * bin_step
11    pub base_factor: u16,
12    /// Filter period determine high frequency trading time window.
13    pub filter_period: u16,
14    /// Decay period determine when the volatile fee start decay / decrease.
15    pub decay_period: u16,
16    /// Reduction factor controls the volatile fee rate decrement rate.
17    pub reduction_factor: u16,
18    /// Used to scale the variable fee component depending on the dynamic of the market
19    pub variable_fee_control: u32,
20    /// Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate.
21    pub max_volatility_accumulator: u32,
22    /// Min bin id supported by the pool based on the configured bin step.
23    pub min_bin_id: i32,
24    /// Max bin id supported by the pool based on the configured bin step.
25    pub max_bin_id: i32,
26    /// Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee
27    pub protocol_share: u16,
28    /// Padding for bytemuck safe alignment
29    pub _padding: [u8; 6],
30}
31
32impl StaticParameters {
33    pub fn update(&mut self, parameter: &FeeParameter) -> Result<()> {
34        let base_factor_delta = if parameter.base_factor > self.base_factor {
35            parameter.base_factor.safe_sub(self.base_factor)?
36        } else {
37            self.base_factor.safe_sub(parameter.base_factor)?
38        };
39
40        // Fee increment / decrement must <= 100% of the current fee rate
41        require!(
42            base_factor_delta <= self.base_factor,
43            LBError::ExcessiveFeeUpdate
44        );
45
46        // Fee increment / decrement must <= 100 bps, 1%
47        require!(
48            base_factor_delta <= MAX_BASE_FACTOR_STEP,
49            LBError::ExcessiveFeeUpdate
50        );
51
52        // During quote it already capped. Extra safety check.
53        require!(
54            parameter.protocol_share <= MAX_PROTOCOL_SHARE,
55            LBError::ExcessiveFeeUpdate
56        );
57
58        self.protocol_share = parameter.protocol_share;
59        self.base_factor = parameter.base_factor;
60
61        Ok(())
62    }
63
64    #[inline(always)]
65    #[cfg(not(feature = "localnet"))]
66    pub fn get_filter_period(&self) -> u16 {
67        self.filter_period
68    }
69
70    #[inline(always)]
71    #[cfg(feature = "localnet")]
72    pub fn get_filter_period(&self) -> u16 {
73        5
74    }
75
76    #[inline(always)]
77    #[cfg(not(feature = "localnet"))]
78    pub fn get_decay_period(&self) -> u16 {
79        self.decay_period
80    }
81
82    #[inline(always)]
83    #[cfg(feature = "localnet")]
84    pub fn get_decay_period(&self) -> u16 {
85        10
86    }
87}
88
89impl Default for StaticParameters {
90    /// These value are references from Trader Joe
91    fn default() -> Self {
92        Self {
93            base_factor: 10_000,
94            filter_period: 30,
95            decay_period: 600,
96            reduction_factor: 500,
97            variable_fee_control: 40_000,
98            protocol_share: 1_000,
99            max_volatility_accumulator: 350_000, // Capped at 35 bin crossed. 350_000 / 10_000 (bps unit) = 35 delta bin
100            _padding: [0u8; 6],
101            max_bin_id: i32::MAX,
102            min_bin_id: i32::MIN,
103        }
104    }
105}
106
107#[zero_copy]
108#[derive(InitSpace, Default, Debug)]
109/// Parameters that changes based on dynamic of the market
110pub struct VariableParameters {
111    /// Volatility accumulator measure the number of bin crossed since reference bin ID. Normally (without filter period taken into consideration), reference bin ID is the active bin of last swap.
112    /// It affects the variable fee rate
113    pub volatility_accumulator: u32,
114    /// Volatility reference is decayed volatility accumulator. It is always <= volatility_accumulator
115    pub volatility_reference: u32,
116    /// Active bin id of last swap.
117    pub index_reference: i32,
118    /// Padding for bytemuck safe alignment
119    pub _padding: [u8; 4],
120    /// Last timestamp the variable parameters was updated
121    pub last_update_timestamp: i64,
122    /// Padding for bytemuck safe alignment
123    pub _padding_1: [u8; 8],
124}
125
126impl VariableParameters {
127    /// volatility_accumulator = min(volatility_reference + num_of_bin_crossed, max_volatility_accumulator)
128    pub fn update_volatility_accumulator(
129        &mut self,
130        active_id: i32,
131        static_params: &StaticParameters,
132    ) -> Result<()> {
133        // Upscale to prevent overflow caused by swapping from left most bin to right most bin.
134        let delta_id = i64::from(self.index_reference)
135            .safe_sub(active_id.into())?
136            .unsigned_abs();
137
138        let volatility_accumulator = u64::from(self.volatility_reference)
139            .safe_add(delta_id.safe_mul(BASIS_POINT_MAX as u64)?)?;
140
141        self.volatility_accumulator = std::cmp::min(
142            volatility_accumulator,
143            static_params.max_volatility_accumulator.into(),
144        )
145        .try_into()
146        .map_err(|_| LBError::TypeCastFailed)?;
147
148        Ok(())
149    }
150
151    /// Update id, and volatility reference
152    pub fn update_references(
153        &mut self,
154        active_id: i32,
155        current_timestamp: i64,
156        static_params: &StaticParameters,
157    ) -> Result<()> {
158        let elapsed = current_timestamp.safe_sub(self.last_update_timestamp)?;
159
160        // Not high frequency trade
161        if elapsed >= static_params.get_filter_period() as i64 {
162            // Update active id of last transaction
163            self.index_reference = active_id;
164            // filter period < t < decay_period. Decay time window.
165            if elapsed < static_params.get_decay_period() as i64 {
166                let volatility_reference = self
167                    .volatility_accumulator
168                    .safe_mul(static_params.reduction_factor as u32)?
169                    .safe_div(BASIS_POINT_MAX as u32)?;
170
171                self.volatility_reference = volatility_reference;
172            }
173            // Out of decay time window
174            else {
175                self.volatility_reference = 0;
176            }
177        }
178
179        // self.last_update_timestamp = current_timestamp;
180
181        Ok(())
182    }
183
184    pub fn update_volatility_parameter(
185        &mut self,
186        active_id: i32,
187        current_timestamp: i64,
188        static_params: &StaticParameters,
189    ) -> Result<()> {
190        self.update_references(active_id, current_timestamp, static_params)?;
191        self.update_volatility_accumulator(active_id, static_params)
192    }
193}