@@ -2,6 +2,9 @@ use cosmwasm_std::{ensure, Decimal, Decimal256, SignedDecimal256, Uint128};
22
33use crate :: TransmuterMathError ;
44
5+
6+
7+
58/// Calculating impact factor component
69///
710/// Considering change of balance of asset $i$, fee/incentive impact factor component $\gamma_i$ is
@@ -24,7 +27,9 @@ use crate::TransmuterMathError;
2427/// \left(\frac{b - \phi_u}{\delta - \phi_u}\right)^2 & \text{if } \phi_u \lt b \leq \delta
2528/// \end{cases}
2629/// $$
27- pub fn calculate_cumulative_impact_factor_component (
30+ ///
31+ /// This function returns √C(b) to delay precision loss handling
32+ pub fn calculate_sqrt_cumulative_impact_factor_component (
2833 normalized_balance : Decimal ,
2934 ideal_balance_lower_bound : Decimal ,
3035 ideal_balance_upper_bound : Decimal ,
@@ -41,15 +46,13 @@ pub fn calculate_cumulative_impact_factor_component(
4146 ideal_balance_lower_bound // phi_l
4247 . checked_sub ( normalized_balance) ? // - b
4348 . checked_div ( ideal_balance_lower_bound) ? // / phi_l
44- . checked_pow ( 2 ) ? // ^2
4549 } else if normalized_balance > ideal_balance_upper_bound {
4650 normalized_balance // b
4751 . checked_sub ( ideal_balance_upper_bound) ? // - phi_u
4852 // delta - phi_u will never be 0 as this case requires b > phi_u,
4953 // delta - phi_u = 0 then delta = phi_u
5054 // since b > delta is restricted by limiter, and delta <= phi_u, this will never happen
5155 . checked_div ( upper_limit. checked_sub ( ideal_balance_upper_bound) ?) ? // / delta - phi_u
52- . checked_pow ( 2 ) ? // ^2
5356 } else {
5457 // within ideal balance
5558 Decimal :: zero ( )
@@ -120,26 +123,39 @@ impl ImpactFactorParamGroup {
120123 }
121124
122125 fn calculate_impact_factor_component ( & self ) -> Result < SignedDecimal256 , TransmuterMathError > {
123- // C(b)
124- let c_b = SignedDecimal256 :: from ( calculate_cumulative_impact_factor_component (
126+ // √ C(b)
127+ let sqrt_c_b = SignedDecimal256 :: from ( calculate_sqrt_cumulative_impact_factor_component (
125128 self . prev_normalized_balance ,
126129 self . ideal_balance_lower_bound ,
127130 self . ideal_balance_upper_bound ,
128131 self . upper_limit ,
129132 ) ?) ;
130133
131- // C(b')
132- let c_b_prime = SignedDecimal256 :: from ( calculate_cumulative_impact_factor_component (
134+ // √ C(b')
135+ let sqrt_c_b_prime = SignedDecimal256 :: from ( calculate_sqrt_cumulative_impact_factor_component (
133136 self . update_normalized_balance ,
134137 self . ideal_balance_lower_bound ,
135138 self . ideal_balance_upper_bound ,
136139 self . upper_limit ,
137140 ) ?) ;
138141
142+
139143 // \gamma_i = C(b') - C(b)
140- c_b_prime
141- . checked_sub ( c_b)
142- . map_err ( TransmuterMathError :: OverflowError )
144+ let c_b_prime = sqrt_c_b_prime. checked_pow ( 2 ) ?;
145+ let c_b = sqrt_c_b. checked_pow ( 2 ) ?;
146+ let gamma_i = c_b_prime. checked_sub ( c_b) ?;
147+
148+ // gamma_i = 0 might be due to precision loss after squaring
149+ // if C(b') - C(b) > 0 is counted as fee factor
150+ // round to most precise positive number representable in SignedDecimal256
151+ //
152+ // C(b') - C(b) < 0 case is not handled here, as it will be counted as incentive factor
153+ // keep it as 0 to prevent overincentive
154+ if gamma_i. is_zero ( ) && sqrt_c_b_prime > sqrt_c_b {
155+ return Ok ( SignedDecimal256 :: raw ( 1 ) ) ;
156+ }
157+
158+ Ok ( gamma_i)
143159 }
144160}
145161
@@ -460,12 +476,12 @@ mod tests {
460476 #[ case] upper_limit : Decimal ,
461477 #[ case] expected : Result < Decimal , TransmuterMathError > ,
462478 ) {
463- let actual = calculate_cumulative_impact_factor_component (
479+ let actual = calculate_sqrt_cumulative_impact_factor_component (
464480 normalized_balance,
465481 ideal_balance_lower_bound,
466482 ideal_balance_upper_bound,
467483 upper_limit,
468- ) ;
484+ ) . map ( |x| x . pow ( 2 ) ) ;
469485 assert_eq ! ( expected, actual) ;
470486 }
471487
@@ -484,7 +500,7 @@ mod tests {
484500 let ideal_balance_upper_bound = Decimal :: raw( ideal_balance_upper_bound) ;
485501 let upper_limit = Decimal :: raw( upper_limit) ;
486502
487- match calculate_cumulative_impact_factor_component (
503+ match calculate_sqrt_cumulative_impact_factor_component (
488504 normalized_balance,
489505 ideal_balance_lower_bound,
490506 ideal_balance_upper_bound,
@@ -505,7 +521,7 @@ mod tests {
505521 let ideal_balance_lower_bound = Decimal :: raw( normalized_balance. saturating_sub( ideal_balance_lower_bound_from_normalized_balance) ) ;
506522 let normalized_balance = Decimal :: raw( normalized_balance) ;
507523 let upper_limit = ideal_balance_upper_bound;
508- let actual = calculate_cumulative_impact_factor_component (
524+ let actual = calculate_sqrt_cumulative_impact_factor_component (
509525 normalized_balance,
510526 ideal_balance_lower_bound,
511527 ideal_balance_upper_bound,
@@ -531,14 +547,14 @@ mod tests {
531547 let upper_limit = Decimal :: raw( upper_limit) ;
532548 let epsilon = Decimal :: raw( 1000u128 ) ;
533549
534- let c1 = calculate_cumulative_impact_factor_component (
550+ let c1 = calculate_sqrt_cumulative_impact_factor_component (
535551 normalized_balance - epsilon,
536552 ideal_balance_lower_bound,
537553 ideal_balance_upper_bound,
538554 upper_limit,
539555 ) . unwrap( ) ;
540556
541- let c2 = calculate_cumulative_impact_factor_component (
557+ let c2 = calculate_sqrt_cumulative_impact_factor_component (
542558 normalized_balance,
543559 ideal_balance_lower_bound,
544560 ideal_balance_upper_bound,
@@ -565,14 +581,14 @@ mod tests {
565581 let upper_limit = Decimal :: raw( upper_limit) ;
566582 let epsilon = Decimal :: raw( 1000u128 ) ;
567583
568- let c1 = calculate_cumulative_impact_factor_component (
584+ let c1 = calculate_sqrt_cumulative_impact_factor_component (
569585 normalized_balance - epsilon,
570586 ideal_balance_lower_bound,
571587 ideal_balance_upper_bound,
572588 upper_limit,
573589 ) . unwrap( ) ;
574590
575- let c2 = calculate_cumulative_impact_factor_component (
591+ let c2 = calculate_sqrt_cumulative_impact_factor_component (
576592 normalized_balance,
577593 ideal_balance_lower_bound,
578594 ideal_balance_upper_bound,
@@ -729,14 +745,40 @@ mod tests {
729745 Decimal :: one( ) ,
730746 Ok ( SignedDecimal256 :: from_str( "0.078125" ) . unwrap( ) )
731747 ) ]
732- // #[case::precision_issue(
733- // Decimal::from_str("0.600000000000000001").unwrap(),
734- // Decimal::from_str("0.600000000000000002").unwrap(),
735- // Decimal::percent(40),
736- // Decimal::percent(60),
737- // Decimal::one(),
738- // Ok(SignedDecimal256::from_str("-0.000000000000000001").unwrap())
739- // )]
748+ // precision loss for fee impact factor >> 0.000000000000000001
749+ #[ case:: precision_loss_positive_impact(
750+ Decimal :: from_str( "0.600000000000000001" ) . unwrap( ) ,
751+ Decimal :: from_str( "0.600000000000000002" ) . unwrap( ) ,
752+ Decimal :: percent( 40 ) ,
753+ Decimal :: percent( 60 ) ,
754+ Decimal :: one( ) ,
755+ Ok ( SignedDecimal256 :: from_str( "0.000000000000000001" ) . unwrap( ) )
756+ ) ]
757+ #[ case:: precision_loss_positive_impact(
758+ Decimal :: from_str( "0.499999999999999999" ) . unwrap( ) ,
759+ Decimal :: from_str( "0.600000000000000001" ) . unwrap( ) ,
760+ Decimal :: percent( 40 ) ,
761+ Decimal :: percent( 60 ) ,
762+ Decimal :: one( ) ,
763+ Ok ( SignedDecimal256 :: from_str( "0.000000000000000001" ) . unwrap( ) )
764+ ) ]
765+ // precision loss for incentive impact factor >> 0
766+ #[ case:: precision_loss_negative_impact(
767+ Decimal :: from_str( "0.600000000000000002" ) . unwrap( ) ,
768+ Decimal :: from_str( "0.600000000000000001" ) . unwrap( ) ,
769+ Decimal :: percent( 40 ) ,
770+ Decimal :: percent( 60 ) ,
771+ Decimal :: one( ) ,
772+ Ok ( SignedDecimal256 :: zero( ) )
773+ ) ]
774+ #[ case:: precision_loss_negative_impact(
775+ Decimal :: from_str( "0.600000000000000001" ) . unwrap( ) ,
776+ Decimal :: from_str( "0.499999999999999999" ) . unwrap( ) ,
777+ Decimal :: percent( 40 ) ,
778+ Decimal :: percent( 60 ) ,
779+ Decimal :: one( ) ,
780+ Ok ( SignedDecimal256 :: zero( ) )
781+ ) ]
740782 fn test_calculate_impact_factor_component (
741783 #[ case] prev_normalized_balance : Decimal ,
742784 #[ case] update_normalized_balance : Decimal ,
0 commit comments