pallet_staking_reward_fn/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
19
20use sp_arithmetic::{
23 biguint::BigUint,
24 traits::{SaturatedConversion, Zero},
25 PerThing, Perquintill,
26};
27
28pub fn compute_inflation<P: PerThing>(stake: P, ideal_stake: P, falloff: P) -> P {
55 if stake < ideal_stake {
56 return stake / ideal_stake
58 }
59
60 if falloff < P::from_percent(1.into()) {
61 log::error!("Invalid inflation computation: falloff less than 1% is not supported");
62 return PerThing::zero()
63 }
64
65 let accuracy = {
66 let mut a = BigUint::from(Into::<u128>::into(P::ACCURACY));
67 a.lstrip();
68 a
69 };
70
71 let mut falloff = BigUint::from(falloff.deconstruct().into());
72 falloff.lstrip();
73
74 let ln2 = {
75 const LN2: u64 = 0_693_147_180_559_945_309;
77 let ln2 = P::from_rational(LN2.into(), Perquintill::ACCURACY.into());
78 BigUint::from(ln2.deconstruct().into())
79 };
80
81 let ln2_div_d = div_by_stripped(ln2.mul(&accuracy), &falloff);
83
84 let inpos_param = INPoSParam {
85 x_ideal: BigUint::from(ideal_stake.deconstruct().into()),
86 x: BigUint::from(stake.deconstruct().into()),
87 accuracy,
88 ln2_div_d,
89 };
90
91 let res = compute_taylor_serie_part(&inpos_param);
92
93 match u128::try_from(res.clone()) {
94 Ok(res) if res <= Into::<u128>::into(P::ACCURACY) => P::from_parts(res.saturated_into()),
95 _ => {
97 log::error!("Invalid inflation computation: unexpected result {:?}", res);
98 P::zero()
99 },
100 }
101}
102
103struct INPoSParam {
107 ln2_div_d: BigUint,
108 x_ideal: BigUint,
109 x: BigUint,
110 accuracy: BigUint,
112}
113
114fn compute_taylor_serie_part(p: &INPoSParam) -> BigUint {
120 let mut last_taylor_term = p.accuracy.clone();
122
123 let mut taylor_sum_positive = true;
125
126 let mut taylor_sum = last_taylor_term.clone();
128
129 for k in 1..300 {
130 last_taylor_term = compute_taylor_term(k, &last_taylor_term, p);
131
132 if last_taylor_term.is_zero() {
133 break
134 }
135
136 let last_taylor_term_positive = k % 2 == 0;
137
138 if taylor_sum_positive == last_taylor_term_positive {
139 taylor_sum = taylor_sum.add(&last_taylor_term);
140 } else if taylor_sum >= last_taylor_term {
141 taylor_sum = taylor_sum
142 .sub(&last_taylor_term)
143 .unwrap_or_else(|e| e);
145 } else {
146 taylor_sum_positive = !taylor_sum_positive;
147 taylor_sum = last_taylor_term
148 .clone()
149 .sub(&taylor_sum)
150 .unwrap_or_else(|e| e);
152 }
153 }
154
155 if !taylor_sum_positive {
156 return BigUint::zero()
157 }
158
159 taylor_sum.lstrip();
160 taylor_sum
161}
162
163fn compute_taylor_term(k: u32, previous_taylor_term: &BigUint, p: &INPoSParam) -> BigUint {
174 let x_minus_x_ideal =
175 p.x.clone()
176 .sub(&p.x_ideal)
177 .unwrap_or_else(|_| BigUint::zero());
179
180 let res = previous_taylor_term.clone().mul(&x_minus_x_ideal).mul(&p.ln2_div_d).div_unit(k);
181
182 let res = div_by_stripped(res, &p.accuracy);
184 let mut res = div_by_stripped(res, &p.accuracy);
185
186 res.lstrip();
187 res
188}
189
190fn div_by_stripped(mut a: BigUint, b: &BigUint) -> BigUint {
194 a.lstrip();
195
196 if b.len() == 0 {
197 log::error!("Computation error: Invalid division");
198 return BigUint::zero()
199 }
200
201 if b.len() == 1 {
202 return a.div_unit(b.checked_get(0).unwrap_or(1))
203 }
204
205 if b.len() > a.len() {
206 return BigUint::zero()
207 }
208
209 if b.len() == a.len() {
210 let mut new_a = a.mul(&BigUint::from(100_000u64.pow(2)));
212 new_a.lstrip();
213
214 debug_assert!(new_a.len() > b.len());
215 return new_a
216 .div(b, false)
217 .map(|res| res.0)
218 .unwrap_or_else(BigUint::zero)
219 .div_unit(100_000)
220 .div_unit(100_000)
221 }
222
223 a.div(b, false).map(|res| res.0).unwrap_or_else(BigUint::zero)
224}