balancer_maths_rust/hooks/exit_fee/
mod.rs1use crate::common::maths::mul_down_fixed;
4use crate::common::types::{HookStateBase, RemoveLiquidityKind};
5use crate::hooks::types::{AfterRemoveLiquidityResult, HookState};
6use crate::hooks::{DefaultHook, HookBase, HookConfig};
7use alloy_primitives::U256;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct ExitFeeHookState {
13 pub hook_type: String,
15 pub tokens: Vec<String>,
17 pub remove_liquidity_hook_fee_percentage: U256,
19}
20
21impl HookStateBase for ExitFeeHookState {
22 fn hook_type(&self) -> &str {
23 &self.hook_type
24 }
25}
26
27impl Default for ExitFeeHookState {
28 fn default() -> Self {
29 Self {
30 hook_type: "ExitFee".to_string(),
31 tokens: vec![],
32 remove_liquidity_hook_fee_percentage: U256::ZERO,
33 }
34 }
35}
36
37pub struct ExitFeeHook {
40 config: HookConfig,
41}
42
43impl ExitFeeHook {
44 pub fn new() -> Self {
45 let config = HookConfig {
46 should_call_after_remove_liquidity: true,
47 enable_hook_adjusted_amounts: true,
48 ..Default::default()
49 };
50
51 Self { config }
52 }
53}
54
55impl HookBase for ExitFeeHook {
56 fn hook_type(&self) -> &str {
57 "ExitFee"
58 }
59
60 fn config(&self) -> &HookConfig {
61 &self.config
62 }
63
64 fn on_after_remove_liquidity(
65 &self,
66 kind: RemoveLiquidityKind,
67 _bpt_amount_in: &U256,
68 _amounts_out_scaled_18: &[U256],
69 amounts_out_raw: &[U256],
70 _balances_scaled_18: &[U256],
71 hook_state: &HookState,
72 ) -> AfterRemoveLiquidityResult {
73 match hook_state {
74 HookState::ExitFee(state) => {
75 if kind != RemoveLiquidityKind::Proportional {
79 return AfterRemoveLiquidityResult {
80 success: false,
81 hook_adjusted_amounts_out_raw: amounts_out_raw.to_vec(),
82 };
83 }
84
85 let mut accrued_fees = vec![U256::ZERO; state.tokens.len()];
86 let mut hook_adjusted_amounts_out_raw = amounts_out_raw.to_vec();
87
88 if state.remove_liquidity_hook_fee_percentage > U256::ZERO {
89 for i in 0..amounts_out_raw.len() {
91 let Ok(hook_fee) = mul_down_fixed(
92 &amounts_out_raw[i],
93 &state.remove_liquidity_hook_fee_percentage,
94 ) else {
95 return AfterRemoveLiquidityResult {
96 success: false,
97 hook_adjusted_amounts_out_raw: amounts_out_raw.to_vec(),
98 };
99 };
100
101 accrued_fees[i] = hook_fee;
102 hook_adjusted_amounts_out_raw[i] -= hook_fee;
103 }
105
106 }
118
119 AfterRemoveLiquidityResult {
120 success: true,
121 hook_adjusted_amounts_out_raw,
122 }
123 }
124 _ => AfterRemoveLiquidityResult {
125 success: false,
126 hook_adjusted_amounts_out_raw: amounts_out_raw.to_vec(),
127 },
128 }
129 }
130
131 fn on_before_add_liquidity(
133 &self,
134 kind: crate::common::types::AddLiquidityKind,
135 max_amounts_in_scaled_18: &[U256],
136 min_bpt_amount_out: &U256,
137 balances_scaled_18: &[U256],
138 hook_state: &HookState,
139 ) -> crate::hooks::types::BeforeAddLiquidityResult {
140 DefaultHook::new().on_before_add_liquidity(
141 kind,
142 max_amounts_in_scaled_18,
143 min_bpt_amount_out,
144 balances_scaled_18,
145 hook_state,
146 )
147 }
148
149 fn on_after_add_liquidity(
150 &self,
151 kind: crate::common::types::AddLiquidityKind,
152 amounts_in_scaled_18: &[U256],
153 amounts_in_raw: &[U256],
154 bpt_amount_out: &U256,
155 balances_scaled_18: &[U256],
156 hook_state: &HookState,
157 ) -> crate::hooks::types::AfterAddLiquidityResult {
158 DefaultHook::new().on_after_add_liquidity(
159 kind,
160 amounts_in_scaled_18,
161 amounts_in_raw,
162 bpt_amount_out,
163 balances_scaled_18,
164 hook_state,
165 )
166 }
167
168 fn on_before_remove_liquidity(
169 &self,
170 kind: crate::common::types::RemoveLiquidityKind,
171 max_bpt_amount_in: &U256,
172 min_amounts_out_scaled_18: &[U256],
173 balances_scaled_18: &[U256],
174 hook_state: &HookState,
175 ) -> crate::hooks::types::BeforeRemoveLiquidityResult {
176 DefaultHook::new().on_before_remove_liquidity(
177 kind,
178 max_bpt_amount_in,
179 min_amounts_out_scaled_18,
180 balances_scaled_18,
181 hook_state,
182 )
183 }
184
185 fn on_before_swap(
186 &self,
187 swap_params: &crate::common::types::SwapParams,
188 hook_state: &HookState,
189 ) -> crate::hooks::types::BeforeSwapResult {
190 DefaultHook::new().on_before_swap(swap_params, hook_state)
191 }
192
193 fn on_after_swap(
194 &self,
195 after_swap_params: &crate::hooks::types::AfterSwapParams,
196 hook_state: &HookState,
197 ) -> crate::hooks::types::AfterSwapResult {
198 DefaultHook::new().on_after_swap(after_swap_params, hook_state)
199 }
200
201 fn on_compute_dynamic_swap_fee(
202 &self,
203 swap_params: &crate::common::types::SwapParams,
204 static_swap_fee_percentage: &U256,
205 hook_state: &HookState,
206 ) -> crate::hooks::types::DynamicSwapFeeResult {
207 DefaultHook::new().on_compute_dynamic_swap_fee(
208 swap_params,
209 static_swap_fee_percentage,
210 hook_state,
211 )
212 }
213}
214
215impl Default for ExitFeeHook {
216 fn default() -> Self {
217 ExitFeeHook::new()
218 }
219}