balancer_maths_rust/pools/liquidity_bootstrapping/
liquidity_bootstrapping_pool.rs1use crate::common::errors::PoolError;
4use crate::common::pool_base::PoolBase;
5use crate::common::types::{Rounding, SwapParams};
6use crate::pools::liquidity_bootstrapping::liquidity_bootstrapping_data::LiquidityBootstrappingState;
7use crate::pools::liquidity_bootstrapping::liquidity_bootstrapping_math::get_normalized_weights;
8use crate::pools::weighted::weighted_math::{MAX_INVARIANT_RATIO, MIN_INVARIANT_RATIO, *};
9use alloy_primitives::U256;
10
11pub struct LiquidityBootstrappingPool {
13 normalized_weights: Vec<U256>,
15 state: LiquidityBootstrappingState,
17}
18
19impl LiquidityBootstrappingPool {
20 pub fn new(state: LiquidityBootstrappingState) -> Result<Self, PoolError> {
22 if state.immutable.start_weights.len() != 2 || state.immutable.end_weights.len() != 2 {
23 return Err(PoolError::InvalidSwapParameters);
24 }
25
26 let normalized_weights = get_normalized_weights(
28 state.immutable.project_token_index,
29 &state.mutable.current_timestamp,
30 &state.immutable.start_time,
31 &state.immutable.end_time,
32 &state.immutable.start_weights[state.immutable.project_token_index],
33 &state.immutable.end_weights[state.immutable.project_token_index],
34 );
35
36 Ok(Self {
37 normalized_weights,
38 state,
39 })
40 }
41
42 fn is_swap_enabled(&self) -> bool {
44 self.state.mutable.is_swap_enabled
45 }
46
47 fn is_project_token_swap_in_blocked(&self) -> bool {
49 self.state.immutable.is_project_token_swap_in_blocked
50 }
51
52 fn get_normalized_weight_pair(
54 &self,
55 index_in: usize,
56 index_out: usize,
57 ) -> Result<(U256, U256), PoolError> {
58 if index_in >= self.normalized_weights.len() || index_out >= self.normalized_weights.len() {
59 return Err(PoolError::InvalidTokenIndex);
60 }
61
62 let token_in_weight = self.normalized_weights[index_in];
63 let token_out_weight = self.normalized_weights[index_out];
64
65 Ok((token_in_weight, token_out_weight))
66 }
67
68 fn validate_swap(&self, token_in_index: usize) -> Result<(), PoolError> {
70 if !self.is_swap_enabled() {
71 return Err(PoolError::InvalidSwapParameters);
72 }
73
74 if self.is_project_token_swap_in_blocked()
76 && token_in_index == self.state.immutable.project_token_index
77 {
78 return Err(PoolError::InvalidSwapParameters);
79 }
80
81 Ok(())
82 }
83}
84
85impl PoolBase for LiquidityBootstrappingPool {
86 fn on_swap(&self, swap_params: &SwapParams) -> Result<U256, PoolError> {
87 let token_in_index = swap_params.token_in_index;
88 let token_out_index = swap_params.token_out_index;
89
90 if token_in_index >= self.normalized_weights.len()
91 || token_out_index >= self.normalized_weights.len()
92 {
93 return Err(PoolError::InvalidTokenIndex);
94 }
95
96 self.validate_swap(token_in_index)?;
98
99 let balance_in = &swap_params.balances_live_scaled_18[token_in_index];
100 let balance_out = &swap_params.balances_live_scaled_18[token_out_index];
101 let amount_scaled_18 = &swap_params.amount_scaled_18;
102
103 let (weight_in, weight_out) =
104 self.get_normalized_weight_pair(token_in_index, token_out_index)?;
105
106 match swap_params.swap_kind {
107 crate::common::types::SwapKind::GivenIn => compute_out_given_exact_in(
108 balance_in,
109 &weight_in,
110 balance_out,
111 &weight_out,
112 amount_scaled_18,
113 ),
114 crate::common::types::SwapKind::GivenOut => compute_in_given_exact_out(
115 balance_in,
116 &weight_in,
117 balance_out,
118 &weight_out,
119 amount_scaled_18,
120 ),
121 }
122 }
123
124 fn compute_invariant(
125 &self,
126 balances_live_scaled_18: &[U256],
127 rounding: Rounding,
128 ) -> Result<U256, PoolError> {
129 match rounding {
130 Rounding::RoundDown => {
131 compute_invariant_down(&self.normalized_weights, balances_live_scaled_18)
132 }
133 Rounding::RoundUp => {
134 compute_invariant_up(&self.normalized_weights, balances_live_scaled_18)
135 }
136 }
137 }
138
139 fn compute_balance(
140 &self,
141 balances_live_scaled_18: &[U256],
142 token_in_index: usize,
143 invariant_ratio: &U256,
144 ) -> Result<U256, PoolError> {
145 if token_in_index >= balances_live_scaled_18.len()
146 || token_in_index >= self.normalized_weights.len()
147 {
148 return Err(PoolError::InvalidTokenIndex);
149 }
150
151 let current_balance = &balances_live_scaled_18[token_in_index];
152 let weight = &self.normalized_weights[token_in_index];
153
154 compute_balance_out_given_invariant(current_balance, weight, invariant_ratio)
156 }
157
158 fn get_maximum_invariant_ratio(&self) -> U256 {
159 MAX_INVARIANT_RATIO
160 }
161
162 fn get_minimum_invariant_ratio(&self) -> U256 {
163 MIN_INVARIANT_RATIO
164 }
165}
166
167impl From<LiquidityBootstrappingState> for LiquidityBootstrappingPool {
168 fn from(liquidity_bootstrapping_state: LiquidityBootstrappingState) -> Self {
169 Self::new(liquidity_bootstrapping_state)
170 .expect("Failed to create LiquidityBootstrappingPool from state")
171 }
172}