kabu_defi_uniswap_v3_math/
swap_math.rs1use alloy::primitives::{I256, U256};
2
3use crate::{
4 error::UniswapV3MathError,
5 full_math::{mul_div, mul_div_rounding_up},
6 sqrt_price_math::{_get_amount_0_delta, _get_amount_1_delta, get_next_sqrt_price_from_input, get_next_sqrt_price_from_output},
7};
8
9pub fn compute_swap_step(
16 sqrt_ratio_current_x_96: U256,
17 sqrt_ratio_target_x_96: U256,
18 liquidity: u128,
19 amount_remaining: I256,
20 fee_pips: u32,
21) -> Result<(U256, U256, U256, U256), UniswapV3MathError> {
22 let zero_for_one = sqrt_ratio_current_x_96 >= sqrt_ratio_target_x_96;
23 let exact_in = amount_remaining >= I256::ZERO;
24
25 let sqrt_ratio_next_x_96: U256;
26 let mut amount_in = U256::ZERO;
27 let mut amount_out = U256::ZERO;
28
29 if exact_in {
30 let amount_remaining_less_fee = mul_div(
31 amount_remaining.into_raw(),
32 U256::from(1e6 as u32 - fee_pips), U256::from_limbs([1000000, 0, 0, 0]), )?;
35
36 amount_in = if zero_for_one {
37 _get_amount_0_delta(sqrt_ratio_target_x_96, sqrt_ratio_current_x_96, liquidity, true)?
38 } else {
39 _get_amount_1_delta(sqrt_ratio_current_x_96, sqrt_ratio_target_x_96, liquidity, true)?
40 };
41
42 if amount_remaining_less_fee >= amount_in {
43 sqrt_ratio_next_x_96 = sqrt_ratio_target_x_96;
44 } else {
45 sqrt_ratio_next_x_96 =
46 get_next_sqrt_price_from_input(sqrt_ratio_current_x_96, liquidity, amount_remaining_less_fee, zero_for_one)?;
47 }
48 } else {
49 amount_out = if zero_for_one {
50 _get_amount_1_delta(sqrt_ratio_target_x_96, sqrt_ratio_current_x_96, liquidity, false)?
51 } else {
52 _get_amount_0_delta(sqrt_ratio_current_x_96, sqrt_ratio_target_x_96, liquidity, false)?
53 };
54
55 sqrt_ratio_next_x_96 = if (-amount_remaining).into_raw() >= amount_out {
56 sqrt_ratio_target_x_96
57 } else {
58 get_next_sqrt_price_from_output(sqrt_ratio_current_x_96, liquidity, (-amount_remaining).into_raw(), zero_for_one)?
59 };
60 }
61
62 let max = sqrt_ratio_target_x_96 == sqrt_ratio_next_x_96;
63
64 if zero_for_one {
65 if !max || !exact_in {
66 amount_in = _get_amount_0_delta(sqrt_ratio_next_x_96, sqrt_ratio_current_x_96, liquidity, true)?
67 }
68
69 if !max || exact_in {
70 amount_out = _get_amount_1_delta(sqrt_ratio_next_x_96, sqrt_ratio_current_x_96, liquidity, false)?
71 }
72 } else {
73 if !max || !exact_in {
74 amount_in = _get_amount_1_delta(sqrt_ratio_current_x_96, sqrt_ratio_next_x_96, liquidity, true)?
75 }
76
77 if !max || exact_in {
78 amount_out = _get_amount_0_delta(sqrt_ratio_current_x_96, sqrt_ratio_next_x_96, liquidity, false)?
79 }
80 }
81
82 if !exact_in && amount_out > (-amount_remaining).into_raw() {
83 amount_out = (-amount_remaining).into_raw();
84 }
85
86 if exact_in && sqrt_ratio_next_x_96 != sqrt_ratio_target_x_96 {
87 let fee_amount = amount_remaining.into_raw() - amount_in;
88 Ok((sqrt_ratio_next_x_96, amount_in, amount_out, fee_amount))
89 } else {
90 let fee_amount = mul_div_rounding_up(amount_in, U256::from(fee_pips), U256::from(1e6 as u32 - fee_pips))?;
91
92 Ok((sqrt_ratio_next_x_96, amount_in, amount_out, fee_amount))
93 }
94}
95
96#[cfg(test)]
97mod test {
98
99 use crate::sqrt_price_math::{get_next_sqrt_price_from_input, get_next_sqrt_price_from_output};
100 use crate::swap_math::compute_swap_step;
101 use crate::U256_1;
102 use alloy::primitives::{I256, U256};
103 use std::str::FromStr;
104
105 #[allow(unused)]
106 #[test]
107 fn test_compute_swap_step() {
108 let price = U256::from_str("79228162514264337593543950336").unwrap();
112 let price_target = U256::from_str("79623317895830914510639640423").unwrap();
113 let liquidity = 2e18 as u128;
114 let amount = I256::from_str("1000000000000000000").unwrap();
115 let fee = 600;
116 let zero_for_one = false;
117
118 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(price, price_target, liquidity, amount, fee).unwrap();
119
120 assert_eq!(sqrt_p, U256::from_str("79623317895830914510639640423").unwrap());
121
122 assert_eq!(amount_in, U256::from_str("9975124224178055").unwrap());
123 assert_eq!(fee_amount, U256::from_str("5988667735148").unwrap());
124 assert_eq!(amount_out, U256::from_str("9925619580021728").unwrap());
125
126 assert!(amount_in + fee_amount < U256::from_limbs(*amount.as_limbs()));
127
128 let price_after_whole_input_amount = get_next_sqrt_price_from_input(price, liquidity, amount_in, zero_for_one).unwrap();
129
130 assert_eq!(sqrt_p, price_target);
131 assert!(sqrt_p < price_after_whole_input_amount);
132
133 let price = U256::from_str("79228162514264337593543950336").unwrap();
137 let price_target = U256::from_str("79623317895830914510639640423").unwrap();
138 let liquidity = 2e18 as u128;
139 let amount = I256::from_str("-1000000000000000000").unwrap();
140 let fee = 600;
141 let zero_for_one = false;
142
143 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(price, price_target, liquidity, amount, fee).unwrap();
144
145 assert_eq!(amount_in, U256::from_str("9975124224178055").unwrap());
146 assert_eq!(fee_amount, U256::from_str("5988667735148").unwrap());
147 assert_eq!(amount_out, U256::from_str("9925619580021728").unwrap());
148 assert!(amount_out < (amount * -I256::ONE).into_raw());
149
150 assert!(amount_in + fee_amount < U256::from_limbs(*amount.as_limbs()));
151
152 let price_after_whole_output_amount =
153 get_next_sqrt_price_from_output(price, liquidity, (amount * -I256::ONE).into_raw(), zero_for_one).unwrap();
154
155 assert_eq!(sqrt_p, price_target);
156 assert!(sqrt_p < price_after_whole_output_amount);
157
158 let price = U256::from_str("79228162514264337593543950336").unwrap();
162 let price_target = U256::from_str("0xe6666666666666666666666666").unwrap();
163 let liquidity = 2e18 as u128;
164 let amount = I256::from_str("1000000000000000000").unwrap();
165 let fee = 600;
166 let zero_for_one = false;
167
168 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(price, price_target, liquidity, amount, fee).unwrap();
169
170 assert_eq!(amount_in, U256::from_str("999400000000000000").unwrap());
171 assert_eq!(fee_amount, U256::from_str("600000000000000").unwrap());
172 assert_eq!(amount_out, U256::from_str("666399946655997866").unwrap());
173 assert_eq!(amount_in + fee_amount, amount.into_raw());
174
175 let price_after_whole_input_amount_less_fee =
176 get_next_sqrt_price_from_input(price, liquidity, (amount - I256::from_raw(fee_amount)).into_raw(), zero_for_one).unwrap();
177
178 assert!(sqrt_p < price_target);
179 assert_eq!(sqrt_p, price_after_whole_input_amount_less_fee);
180
181 let price = U256::from_str("79228162514264337593543950336").unwrap();
185 let price_target = U256::from_str("792281625142643375935439503360").unwrap();
186 let liquidity = 2e18 as u128;
187 let amount = I256::from_str("1000000000000000000").unwrap() * -I256::ONE;
188 let fee = 600;
189 let zero_for_one = false;
190
191 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(price, price_target, liquidity, amount, fee).unwrap();
192
193 assert_eq!(amount_in, U256::from_str("2000000000000000000").unwrap());
194 assert_eq!(fee_amount, U256::from_str("1200720432259356").unwrap());
195 assert_eq!(amount_out, (amount * -I256::ONE).into_raw());
196
197 let price_after_whole_output_amount =
198 get_next_sqrt_price_from_output(price, liquidity, (amount * -I256::ONE).into_raw(), zero_for_one).unwrap();
199 assert!(sqrt_p < price_target);
204 println!("sqrtp: {sqrt_p:?}, price_after_whole output amount: {price_after_whole_output_amount:?}");
206 assert_eq!(sqrt_p, price_after_whole_output_amount);
207
208 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(
212 U256::from_str("417332158212080721273783715441582").unwrap(),
213 U256::from_str("1452870262520218020823638996").unwrap(),
214 159344665391607089467575320103_u128,
215 I256::from_str("-1").unwrap(),
216 1,
217 )
218 .unwrap();
219
220 assert_eq!(amount_in, U256::from_str("1").unwrap());
221 assert_eq!(fee_amount, U256::from_str("1").unwrap());
222 assert_eq!(amount_out, U256::from_str("1").unwrap());
223 assert_eq!(sqrt_p, U256::from_str("417332158212080721273783715441581").unwrap());
224
225 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(
229 U256::from_str("2").unwrap(),
230 U256::from_str("1").unwrap(),
231 1_u128,
232 I256::from_str("3915081100057732413702495386755767").unwrap(),
233 1,
234 )
235 .unwrap();
236
237 assert_eq!(amount_in, U256::from_str("39614081257132168796771975168").unwrap());
238 assert_eq!(fee_amount, U256::from_str("39614120871253040049813").unwrap());
239 assert!(amount_in + fee_amount < U256::from_str("3915081100057732413702495386755767").unwrap());
240 assert_eq!(amount_out, U256::from_str("0").unwrap());
241
242 assert_eq!(sqrt_p, U256::from_str("1").unwrap());
243
244 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(
248 U256::from_str("2413").unwrap(),
249 U256::from_str("79887613182836312").unwrap(),
250 1985041575832132834610021537970_u128,
251 I256::from_str("10").unwrap(),
252 1872,
253 )
254 .unwrap();
255
256 assert_eq!(amount_in, U256::from_str("0").unwrap());
257 assert_eq!(fee_amount, U256::from_str("10").unwrap());
258 assert_eq!(amount_out, U256::from_str("0").unwrap());
259 assert_eq!(sqrt_p, U256::from_str("2413").unwrap());
260
261 let price = U256::from_str("20282409603651670423947251286016").unwrap();
266 let price_target = price * U256::from(11) / U256::from(10);
267 let liquidity = 1024;
268 let amount_remaining = -I256::from_limbs(*U256::from(4).as_limbs());
271 let fee = 3000;
272
273 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(price, price_target, liquidity, amount_remaining, fee).unwrap();
274
275 assert_eq!(amount_out, U256::ZERO);
276 assert_eq!(sqrt_p, price_target);
277 assert_eq!(amount_in, U256::from(26215));
278 assert_eq!(fee_amount, U256::from(79));
279
280 let price = U256::from_str("20282409603651670423947251286016").unwrap();
285
286 let price_target = price * U256::from(9) / U256::from(10);
287 let liquidity = 1024;
288 let amount_remaining = -I256::from_limbs(*U256::from(263000).as_limbs());
291 let fee = 3000;
292
293 let (sqrt_p, amount_in, amount_out, fee_amount) = compute_swap_step(price, price_target, liquidity, amount_remaining, fee).unwrap();
294
295 assert_eq!(amount_out, U256::from(26214));
296 assert_eq!(sqrt_p, price_target);
297 assert_eq!(amount_in, U256_1);
298 assert_eq!(fee_amount, U256_1);
299 }
300}