1use num_traits::{CheckedDiv, Zero};
2
3use crate::{
4 fixed::FixedPointOps,
5 market::{BaseMarket, BaseMarketExt, PerpMarketMutExt},
6 num::{MulDiv, Unsigned},
7 params::fee::FundingRateChangeType,
8 price::Prices,
9 Balance, BalanceExt, PerpMarketMut,
10};
11
12use super::MarketAction;
13
14#[must_use = "actions do nothing unless you `execute` them"]
16pub struct UpdateFundingState<M: BaseMarket<DECIMALS>, const DECIMALS: u8> {
17 market: M,
18 prices: Prices<M::Num>,
19}
20
21impl<M: PerpMarketMut<DECIMALS>, const DECIMALS: u8> UpdateFundingState<M, DECIMALS> {
22 pub fn try_new(market: M, prices: &Prices<M::Num>) -> crate::Result<Self> {
24 prices.validate()?;
25 Ok(Self {
26 market,
27 prices: prices.clone(),
28 })
29 }
30
31 pub fn next_funding_amount_per_size(
33 &self,
34 duration_in_seconds: u64,
35 ) -> crate::Result<UpdateFundingReport<M::Num, <M::Num as Unsigned>::Signed>> {
36 use crate::utils;
37 use num_traits::{CheckedMul, FromPrimitive};
38
39 let mut report = UpdateFundingReport::empty(duration_in_seconds);
40 let open_interest = self.market.open_interest()?;
41 let long_open_interest = open_interest.long_amount()?;
42 let short_open_interest = open_interest.short_amount()?;
43
44 if long_open_interest.is_zero() || short_open_interest.is_zero() {
45 return Ok(report);
46 }
47
48 let (funding_factor_per_second, longs_pay_shorts, next_funding_factor_per_second) = self
49 .next_funding_factor_per_second(
50 duration_in_seconds,
51 &long_open_interest,
52 &short_open_interest,
53 )?;
54 report.next_funding_factor_per_second = next_funding_factor_per_second;
55
56 let size_of_larger_side = if long_open_interest > short_open_interest {
57 long_open_interest.clone()
58 } else {
59 short_open_interest.clone()
60 };
61 let duration_value = M::Num::from_u64(duration_in_seconds).ok_or(crate::Error::Convert)?;
62 let funding_factor = duration_value
63 .checked_mul(&funding_factor_per_second)
64 .ok_or(crate::Error::Computation("calculating funding factor"))?;
65 let funding_value = utils::apply_factor(&size_of_larger_side, &funding_factor)
66 .ok_or(crate::Error::Computation("calculating funding value"))?;
67
68 let payer_open_interest = if longs_pay_shorts {
69 &long_open_interest
70 } else {
71 &short_open_interest
72 };
73 let for_long_collateral = funding_value
74 .checked_mul_div(
75 &self
76 .market
77 .open_interest_pool(longs_pay_shorts)?
78 .long_amount()?,
79 payer_open_interest,
80 )
81 .ok_or(crate::Error::Computation(
82 "calculating funding value for long collateral",
83 ))?;
84 let for_short_collateral = funding_value
85 .checked_mul_div(
86 &self
87 .market
88 .open_interest_pool(longs_pay_shorts)?
89 .short_amount()?,
90 payer_open_interest,
91 )
92 .ok_or(crate::Error::Computation(
93 "calculating funding value for short collateral",
94 ))?;
95
96 self.set_deltas(
97 &mut report,
98 longs_pay_shorts,
99 &for_long_collateral,
100 &for_short_collateral,
101 if !longs_pay_shorts {
102 &long_open_interest
103 } else {
104 &short_open_interest
105 },
106 )?;
107
108 Ok(report)
109 }
110
111 fn set_deltas(
112 &self,
113 report: &mut UpdateFundingReport<M::Num, <M::Num as Unsigned>::Signed>,
114 longs_pay_shorts: bool,
115 for_long_collateral: &M::Num,
116 for_short_collateral: &M::Num,
117 receiver_interest: &M::Num,
118 ) -> crate::Result<()> {
119 let adjustment = &self.market.funding_amount_per_size_adjustment();
120 for is_long_collateral in [true, false] {
121 let (funding_value, price) = if is_long_collateral {
122 (
123 for_long_collateral,
124 self.prices.long_token_price.pick_price(true),
125 )
126 } else {
127 (
128 for_short_collateral,
129 self.prices.short_token_price.pick_price(true),
130 )
131 };
132
133 let payer = flags_to_index(longs_pay_shorts, is_long_collateral);
134 let receiver = flags_to_index(!longs_pay_shorts, is_long_collateral);
135
136 report.delta_funding_amount_per_size[payer] = pack_to_funding_amount_per_size(
137 adjustment,
138 funding_value,
139 &self
140 .market
141 .open_interest_pool(longs_pay_shorts)?
142 .amount(is_long_collateral)?,
143 price,
144 true,
145 )
146 .ok_or(crate::Error::Computation(
147 "calculating delta funding amount per size",
148 ))?;
149
150 report.delta_claimable_funding_amount_per_size[receiver] =
151 pack_to_funding_amount_per_size(
152 adjustment,
153 funding_value,
154 receiver_interest,
155 price,
156 false,
157 )
158 .ok_or(crate::Error::Computation(
159 "calculating delta claimable funding amount per size",
160 ))?;
161 }
162 Ok(())
163 }
164
165 pub fn next_funding_factor_per_second(
167 &self,
168 duration_in_seconds: u64,
169 long_open_interest: &M::Num,
170 short_open_interest: &M::Num,
171 ) -> crate::Result<(M::Num, bool, M::Signed)> {
172 use crate::{num::UnsignedAbs, utils};
173 use num_traits::{CheckedAdd, CheckedMul, CheckedSub, FromPrimitive, Signed};
174
175 let params = self.market.funding_fee_params()?;
176 let funding_increase_factor_per_second = params.increase_factor_per_second();
177
178 let diff_value = long_open_interest.clone().diff(short_open_interest.clone());
179
180 if diff_value.is_zero() && funding_increase_factor_per_second.is_zero() {
181 return Ok((Zero::zero(), true, Zero::zero()));
182 }
183
184 let total_open_interest = long_open_interest
185 .checked_add(short_open_interest)
186 .ok_or(crate::Error::Computation("calculating total open interest"))?;
187
188 if total_open_interest.is_zero() {
189 return Err(crate::Error::UnableToGetFundingFactorEmptyOpenInterest);
190 }
191
192 let diff_value_after_exponent =
193 utils::apply_exponent_factor(diff_value, params.exponent().clone()).ok_or(
194 crate::Error::Computation("applying exponent factor to diff value"),
195 )?;
196 let diff_value_to_open_interest_factor =
197 utils::div_to_factor(&diff_value_after_exponent, &total_open_interest, false).ok_or(
198 crate::Error::Computation("calculating diff value to open interest factor"),
199 )?;
200
201 if funding_increase_factor_per_second.is_zero() {
202 let mut funding_factor_per_second =
203 utils::apply_factor(&diff_value_to_open_interest_factor, params.factor()).ok_or(
204 crate::Error::Computation("calculating fallback funding factor per second"),
205 )?;
206
207 if funding_factor_per_second > *params.max_factor_per_second() {
208 funding_factor_per_second = params.max_factor_per_second().clone();
209 }
210
211 return Ok((
212 funding_factor_per_second,
213 long_open_interest > short_open_interest,
214 Zero::zero(),
215 ));
216 }
217
218 let funding_factor_per_second = self.market.funding_factor_per_second();
219 let funding_factor_per_second_magnitude = funding_factor_per_second.unsigned_abs();
220
221 let change = params.change(
222 funding_factor_per_second,
223 long_open_interest,
224 short_open_interest,
225 &diff_value_to_open_interest_factor,
226 );
227
228 let duration_value = M::Num::from_u64(duration_in_seconds).ok_or(crate::Error::Convert)?;
229 let next_funding_factor_per_second = match change {
230 FundingRateChangeType::Increase => {
231 let increase_value = utils::apply_factor(
232 &diff_value_to_open_interest_factor,
233 funding_increase_factor_per_second,
234 )
235 .and_then(|v| v.checked_mul(&duration_value))
236 .ok_or(crate::Error::Computation(
237 "calculating factor increase value",
238 ))?;
239
240 let increase_value = if long_open_interest < short_open_interest {
241 increase_value.to_opposite_signed()?
242 } else {
243 increase_value.to_signed()?
244 };
245
246 funding_factor_per_second
247 .checked_add(&increase_value)
248 .ok_or(crate::Error::Computation("increasing funding factor"))?
249 }
250 FundingRateChangeType::Decrease if !funding_factor_per_second_magnitude.is_zero() => {
251 let decrease_value = params
252 .decrease_factor_per_second()
253 .checked_mul(&duration_value)
254 .ok_or(crate::Error::Computation(
255 "calculating factor decrease value",
256 ))?;
257 if funding_factor_per_second_magnitude <= decrease_value {
258 funding_factor_per_second
259 .checked_div(&funding_factor_per_second_magnitude.to_signed()?)
260 .ok_or(crate::Error::Computation("calculating signum"))?
261 } else {
262 let decreased = funding_factor_per_second_magnitude
263 .checked_sub(&decrease_value)
264 .ok_or(crate::Error::Computation(
265 "calculating decreased funding factor per second (infallible)",
266 ))?;
267 if funding_factor_per_second.is_negative() {
268 decreased.to_opposite_signed()?
269 } else {
270 decreased.to_signed()?
271 }
272 }
273 }
274 _ => funding_factor_per_second.clone(),
275 };
276
277 let next_funding_factor_per_second = Unsigned::bound_magnitude(
278 &next_funding_factor_per_second,
279 &Zero::zero(),
280 params.max_factor_per_second(),
281 )?;
282
283 let next_funding_factor_per_second_with_min_bound = Unsigned::bound_magnitude(
284 &next_funding_factor_per_second,
285 params.min_factor_per_second(),
286 params.max_factor_per_second(),
287 )?;
288
289 Ok((
290 next_funding_factor_per_second_with_min_bound.unsigned_abs(),
291 next_funding_factor_per_second_with_min_bound.is_positive(),
292 next_funding_factor_per_second,
293 ))
294 }
295}
296
297impl<M: PerpMarketMut<DECIMALS>, const DECIMALS: u8> MarketAction
298 for UpdateFundingState<M, DECIMALS>
299{
300 type Report = UpdateFundingReport<M::Num, <M::Num as Unsigned>::Signed>;
301
302 fn execute(mut self) -> crate::Result<Self::Report> {
303 const MATRIX: [(bool, bool); 4] =
304 [(true, true), (true, false), (false, true), (false, false)];
305 let duration_in_seconds = self.market.just_passed_in_seconds_for_funding()?;
306 let report = self.next_funding_amount_per_size(duration_in_seconds)?;
307 for (is_long, is_long_collateral) in MATRIX {
308 self.market.apply_delta_to_funding_amount_per_size(
309 is_long,
310 is_long_collateral,
311 &report
312 .delta_funding_amount_per_size(is_long, is_long_collateral)
313 .to_signed()?,
314 )?;
315 self.market
316 .apply_delta_to_claimable_funding_amount_per_size(
317 is_long,
318 is_long_collateral,
319 &report
320 .delta_claimable_funding_amount_per_size(is_long, is_long_collateral)
321 .to_signed()?,
322 )?;
323 }
324 *self.market.funding_factor_per_second_mut() =
325 report.next_funding_factor_per_second().clone();
326 Ok(report)
327 }
328}
329
330#[derive(Debug)]
332#[cfg_attr(
333 feature = "anchor-lang",
334 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
335)]
336pub struct UpdateFundingReport<Unsigned, Signed> {
337 duration_in_seconds: u64,
338 next_funding_factor_per_second: Signed,
339 delta_funding_amount_per_size: [Unsigned; 4],
340 delta_claimable_funding_amount_per_size: [Unsigned; 4],
341}
342
343#[cfg(feature = "gmsol-utils")]
344impl<Unsigned, Signed> gmsol_utils::InitSpace for UpdateFundingReport<Unsigned, Signed>
345where
346 Unsigned: gmsol_utils::InitSpace,
347 Signed: gmsol_utils::InitSpace,
348{
349 const INIT_SPACE: usize =
350 u64::INIT_SPACE + Signed::INIT_SPACE + 4 * Unsigned::INIT_SPACE + 4 * Unsigned::INIT_SPACE;
351}
352
353#[inline]
354fn flags_to_index(is_long: bool, is_long_collateral: bool) -> usize {
355 match (is_long_collateral, is_long) {
356 (true, true) => 0,
357 (true, false) => 1,
358 (false, true) => 2,
359 (false, false) => 3,
360 }
361}
362
363impl<T: Unsigned> UpdateFundingReport<T, T::Signed> {
364 pub fn empty(duration_in_seconds: u64) -> Self {
366 Self {
367 duration_in_seconds,
368 next_funding_factor_per_second: Zero::zero(),
369 delta_funding_amount_per_size: [Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero()],
370 delta_claimable_funding_amount_per_size: [
371 Zero::zero(),
372 Zero::zero(),
373 Zero::zero(),
374 Zero::zero(),
375 ],
376 }
377 }
378
379 pub fn duration_in_seconds(&self) -> u64 {
381 self.duration_in_seconds
382 }
383
384 #[inline]
386 pub fn next_funding_factor_per_second(&self) -> &T::Signed {
387 &self.next_funding_factor_per_second
388 }
389
390 #[inline]
392 pub fn delta_funding_amount_per_size(&self, is_long: bool, is_long_collateral: bool) -> &T {
393 let idx = flags_to_index(is_long, is_long_collateral);
394 &self.delta_funding_amount_per_size[idx]
395 }
396
397 #[inline]
399 pub fn delta_claimable_funding_amount_per_size(
400 &self,
401 is_long: bool,
402 is_long_collateral: bool,
403 ) -> &T {
404 let idx = flags_to_index(is_long, is_long_collateral);
405 &self.delta_claimable_funding_amount_per_size[idx]
406 }
407}
408
409pub fn pack_to_funding_amount_per_size<T, const DECIMALS: u8>(
411 adjustment: &T,
412 funding_value: &T,
413 open_interest: &T,
414 price: &T,
415 round_up_magnitude: bool,
416) -> Option<T>
417where
418 T: FixedPointOps<DECIMALS>,
419{
420 if funding_value.is_zero() || open_interest.is_zero() {
421 return Some(Zero::zero());
422 }
423
424 let numerator = adjustment.checked_mul(&T::UNIT)?;
425 let funding_value_per_size = if round_up_magnitude {
426 funding_value.checked_mul_div_ceil(&numerator, open_interest)?
427 } else {
428 funding_value.checked_mul_div(&numerator, open_interest)?
429 };
430
431 debug_assert!(!price.is_zero(), "must be non-zero");
432 if round_up_magnitude {
433 funding_value_per_size.checked_round_up_div(price)
434 } else {
435 funding_value_per_size.checked_div(price)
436 }
437}
438
439pub fn unpack_to_funding_amount_delta<T, const DECIMALS: u8>(
441 adjustment: &T,
442 latest_funding_amount_per_size: &T,
443 position_funding_amount_per_size: &T,
444 size_in_usd: &T,
445 round_up_magnitude: bool,
446) -> Option<T>
447where
448 T: FixedPointOps<DECIMALS>,
449{
450 let funding_diff_factor =
451 latest_funding_amount_per_size.checked_sub(position_funding_amount_per_size)?;
452
453 let adjustment = adjustment.checked_mul(&T::UNIT)?;
454 if round_up_magnitude {
455 size_in_usd.checked_mul_div_ceil(&funding_diff_factor, &adjustment)
456 } else {
457 size_in_usd.checked_mul_div(&funding_diff_factor, &adjustment)
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use std::{thread::sleep, time::Duration};
464
465 use crate::{
466 market::LiquidityMarketMutExt,
467 test::{TestMarket, TestPosition},
468 MarketAction, PositionMutExt,
469 };
470
471 use super::*;
472
473 #[test]
474 fn test_update_funding_state() -> crate::Result<()> {
475 let mut market = TestMarket::<u64, 9>::default();
476 let prices = Prices::new_for_test(120, 120, 1);
477 market
478 .deposit(1_000_000_000_000, 100_000_000_000_000, prices)?
479 .execute()?;
480 println!("{market:#?}");
481 let mut long = TestPosition::long(true);
482 let mut short = TestPosition::short(false);
483 let prices = Prices::new_for_test(123, 123, 1);
484 let report = long
485 .ops(&mut market)
486 .increase(prices, 1_000_000_000_000, 50_000_000_000_000, None)?
487 .execute()?;
488 println!("{report:#?}");
489 let report = short
490 .ops(&mut market)
491 .increase(prices, 100_000_000_000_000, 25_000_000_000_000, None)?
492 .execute()?;
493 println!("{report:#?}");
494 println!("{market:#?}");
495 sleep(Duration::from_secs(2));
496 let report = long
497 .ops(&mut market)
498 .decrease(prices, 50_000_000_000_000, None, 0, Default::default())?
499 .execute()?;
500 println!("{report:#?}");
501 let report = short
502 .ops(&mut market)
503 .decrease(prices, 25_000_000_000_000, None, 0, Default::default())?
504 .execute()?;
505 println!("{report:#?}");
506 println!("{market:#?}");
507 Ok(())
508 }
509}