balancer_maths_rust/hooks/akron/
mod.rs1use crate::common::errors::PoolError;
2use crate::common::maths::{div_down_fixed, div_up_fixed, mul_div_up_fixed, pow_up_fixed};
3use crate::common::types::{HookStateBase, SwapKind};
4use crate::hooks::types::{DynamicSwapFeeResult, HookState};
5use crate::hooks::{DefaultHook, HookBase, HookConfig};
6use alloy_primitives::U256;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct AkronHookState {
12 pub hook_type: String,
14 pub weights: Vec<U256>,
16 pub minimum_swap_fee_percentage: U256,
18}
19
20impl HookStateBase for AkronHookState {
21 fn hook_type(&self) -> &str {
22 &self.hook_type
23 }
24}
25
26impl Default for AkronHookState {
27 fn default() -> Self {
28 Self {
29 hook_type: "Akron".to_string(),
30 weights: vec![],
31 minimum_swap_fee_percentage: U256::ZERO,
32 }
33 }
34}
35
36pub struct AkronHook {
39 config: HookConfig,
40}
41
42impl AkronHook {
43 pub fn new() -> Self {
44 let config = HookConfig {
45 should_call_compute_dynamic_swap_fee: true,
46 ..Default::default()
47 };
48
49 Self { config }
50 }
51
52 fn compute_swap_fee_percentage_given_exact_in(
54 balance_in: &U256,
55 exponent: &U256,
56 amount_in: &U256,
57 ) -> Result<U256, PoolError> {
58 let balance_plus_amount = balance_in + amount_in;
60 let balance_plus_amount_times_2 = balance_in + amount_in * U256::from(2);
61
62 let power_with_fees = pow_up_fixed(
63 &div_up_fixed(&balance_plus_amount, &balance_plus_amount_times_2)?,
64 exponent,
65 )?;
66 let power_without_fees =
67 pow_up_fixed(&div_up_fixed(balance_in, &balance_plus_amount)?, exponent)?;
68
69 let numerator = mul_div_up_fixed(
70 &balance_plus_amount,
71 &(power_with_fees - power_without_fees),
72 &power_with_fees,
73 )?;
74
75 mul_div_up_fixed(exponent, &numerator, amount_in)
76 }
77
78 fn compute_swap_fee_percentage_given_exact_out(
80 balance_out: &U256,
81 exponent: &U256,
82 amount_out: &U256,
83 ) -> Result<U256, PoolError> {
84 let balance_minus_amount = balance_out - amount_out;
86 let balance_minus_amount_times_2 = balance_out - amount_out * U256::from(2);
87
88 let power_with_fees = pow_up_fixed(
89 &div_up_fixed(&balance_minus_amount, &balance_minus_amount_times_2)?,
90 exponent,
91 )?;
92 let power_without_fees =
93 pow_up_fixed(&div_up_fixed(balance_out, &balance_minus_amount)?, exponent)?;
94
95 let numerator = power_with_fees - power_without_fees;
96 let denominator = power_with_fees - crate::common::constants::WAD;
97
98 div_up_fixed(&numerator, &denominator)
99 }
100}
101
102impl HookBase for AkronHook {
103 fn hook_type(&self) -> &str {
104 "Akron"
105 }
106
107 fn config(&self) -> &HookConfig {
108 &self.config
109 }
110
111 fn on_compute_dynamic_swap_fee(
112 &self,
113 swap_params: &crate::common::types::SwapParams,
114 _static_swap_fee_percentage: &U256,
115 hook_state: &HookState,
116 ) -> DynamicSwapFeeResult {
117 match hook_state {
118 HookState::Akron(state) => {
119 let calculated_swap_fee_percentage = if swap_params.swap_kind == SwapKind::GivenIn {
120 let Ok(exponent) = div_down_fixed(
121 &state.weights[swap_params.token_in_index],
122 &state.weights[swap_params.token_out_index],
123 ) else {
124 return DynamicSwapFeeResult {
125 success: false,
126 dynamic_swap_fee: U256::ZERO,
127 };
128 };
129
130 match Self::compute_swap_fee_percentage_given_exact_in(
131 &swap_params.balances_live_scaled_18[swap_params.token_in_index],
132 &exponent,
133 &swap_params.amount_scaled_18,
134 ) {
135 Ok(fee) => fee,
136 Err(_) => {
137 return DynamicSwapFeeResult {
138 success: false,
139 dynamic_swap_fee: U256::ZERO,
140 };
141 }
142 }
143 } else {
144 let Ok(exponent) = div_up_fixed(
145 &state.weights[swap_params.token_out_index],
146 &state.weights[swap_params.token_in_index],
147 ) else {
148 return DynamicSwapFeeResult {
149 success: false,
150 dynamic_swap_fee: U256::ZERO,
151 };
152 };
153
154 match Self::compute_swap_fee_percentage_given_exact_out(
155 &swap_params.balances_live_scaled_18[swap_params.token_out_index],
156 &exponent,
157 &swap_params.amount_scaled_18,
158 ) {
159 Ok(fee) => fee,
160 Err(_) => {
161 return DynamicSwapFeeResult {
162 success: false,
163 dynamic_swap_fee: U256::ZERO,
164 };
165 }
166 }
167 };
168
169 let dynamic_swap_fee =
171 if state.minimum_swap_fee_percentage > calculated_swap_fee_percentage {
172 state.minimum_swap_fee_percentage
173 } else {
174 calculated_swap_fee_percentage
175 };
176
177 DynamicSwapFeeResult {
178 success: true,
179 dynamic_swap_fee,
180 }
181 }
182 _ => DynamicSwapFeeResult {
183 success: false,
184 dynamic_swap_fee: U256::ZERO,
185 },
186 }
187 }
188
189 fn on_before_add_liquidity(
191 &self,
192 kind: crate::common::types::AddLiquidityKind,
193 max_amounts_in_scaled_18: &[U256],
194 min_bpt_amount_out: &U256,
195 balances_scaled_18: &[U256],
196 hook_state: &HookState,
197 ) -> crate::hooks::types::BeforeAddLiquidityResult {
198 DefaultHook::new().on_before_add_liquidity(
199 kind,
200 max_amounts_in_scaled_18,
201 min_bpt_amount_out,
202 balances_scaled_18,
203 hook_state,
204 )
205 }
206
207 fn on_after_add_liquidity(
208 &self,
209 kind: crate::common::types::AddLiquidityKind,
210 amounts_in_scaled_18: &[U256],
211 amounts_in_raw: &[U256],
212 bpt_amount_out: &U256,
213 balances_scaled_18: &[U256],
214 hook_state: &HookState,
215 ) -> crate::hooks::types::AfterAddLiquidityResult {
216 DefaultHook::new().on_after_add_liquidity(
217 kind,
218 amounts_in_scaled_18,
219 amounts_in_raw,
220 bpt_amount_out,
221 balances_scaled_18,
222 hook_state,
223 )
224 }
225
226 fn on_before_remove_liquidity(
227 &self,
228 kind: crate::common::types::RemoveLiquidityKind,
229 max_bpt_amount_in: &U256,
230 min_amounts_out_scaled_18: &[U256],
231 balances_scaled_18: &[U256],
232 hook_state: &HookState,
233 ) -> crate::hooks::types::BeforeRemoveLiquidityResult {
234 DefaultHook::new().on_before_remove_liquidity(
235 kind,
236 max_bpt_amount_in,
237 min_amounts_out_scaled_18,
238 balances_scaled_18,
239 hook_state,
240 )
241 }
242
243 fn on_after_remove_liquidity(
244 &self,
245 kind: crate::common::types::RemoveLiquidityKind,
246 bpt_amount_in: &U256,
247 amounts_out_scaled_18: &[U256],
248 amounts_out_raw: &[U256],
249 balances_scaled_18: &[U256],
250 hook_state: &HookState,
251 ) -> crate::hooks::types::AfterRemoveLiquidityResult {
252 DefaultHook::new().on_after_remove_liquidity(
253 kind,
254 bpt_amount_in,
255 amounts_out_scaled_18,
256 amounts_out_raw,
257 balances_scaled_18,
258 hook_state,
259 )
260 }
261
262 fn on_before_swap(
263 &self,
264 swap_params: &crate::common::types::SwapParams,
265 hook_state: &HookState,
266 ) -> crate::hooks::types::BeforeSwapResult {
267 DefaultHook::new().on_before_swap(swap_params, hook_state)
268 }
269
270 fn on_after_swap(
271 &self,
272 after_swap_params: &crate::hooks::types::AfterSwapParams,
273 hook_state: &HookState,
274 ) -> crate::hooks::types::AfterSwapResult {
275 DefaultHook::new().on_after_swap(after_swap_params, hook_state)
276 }
277}
278
279impl Default for AkronHook {
280 fn default() -> Self {
281 AkronHook::new()
282 }
283}