Skip to content

Commit

Permalink
Merge branch 'bengt/pos-inflation-fix' (#1756)
Browse files Browse the repository at this point in the history
* origin/bengt/pos-inflation-fix:
  inflation: fix uint conv for negative control
  test/inflation: add tests for inflation calc
  added changelog
  fix inflation calc
  hopefully fixes inflation
  • Loading branch information
Fraccaman committed Jul 26, 2023
2 parents a1fd70f + badc4cb commit c7fbc8c
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 12 deletions.
2 changes: 2 additions & 0 deletions .changelog/unreleased/bug-fixes/1756-pos-inflation-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Fixes how PoS inflation is calculated.
([\#1756](https://github.com/anoma/namada/pull/1756))
187 changes: 175 additions & 12 deletions shared/src/ledger/inflation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ impl RewardsController {

// Token amounts must be expressed in terms of the raw amount (namnam)
// to properly run the PD controller
let locked =
Dec::try_from(locked_tokens.raw_amount()).expect("Should not fail");
let total =
Dec::try_from(total_tokens.raw_amount()).expect("Should not fail");
let locked = Dec::try_from(locked_tokens.raw_amount())
.expect("Should not fail to convert token Amount to Dec");
let total = Dec::try_from(total_tokens.raw_amount())
.expect("Should not fail to convert token Amount to Dec");
let epochs_py: Dec = epochs_per_year.into();

let locked_ratio = locked / total;
Expand All @@ -78,20 +78,183 @@ impl RewardsController {
let delta_error = locked_ratio_last - locked_ratio;
let control_val = p_gain * error - d_gain * delta_error;

let last_inflation_amount = Dec::from(last_inflation_amount);
let new_inflation_amount = last_inflation_amount + control_val;
let inflation = if last_inflation_amount + control_val > max_inflation {
max_inflation
} else if new_inflation_amount > Dec::zero() {
new_inflation_amount
let last_inflation_amount =
Dec::try_from(last_inflation_amount.raw_amount())
.expect("Should not fail to convert token Amount to Dec");
let new_inflation_amount_raw = last_inflation_amount + control_val;
let new_inflation_amount = if new_inflation_amount_raw.is_negative() {
token::Amount::zero()
} else {
Dec::zero()
token::Amount::from_uint(
new_inflation_amount_raw
.to_uint()
.expect("Should not fail to convert Dec to Uint"),
0,
)
.expect("Should not fail to convert Uint to Amount")
};
let inflation = token::Amount::from(inflation);

let max_inflation = token::Amount::from_uint(
max_inflation
.to_uint()
.expect("Should not fail to convert Dec to Uint"),
0,
)
.expect("Should not fail to convert Uint to Amount");

let inflation = std::cmp::min(new_inflation_amount, max_inflation);
ValsToUpdate {
locked_ratio,
inflation,
}
}
}

#[cfg(test)]
mod test {
use std::str::FromStr;

use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES;

use super::*;

#[test]
fn test_inflation_calc_up() {
let mut controller = RewardsController {
locked_tokens: token::Amount::from_uint(
2_000,
NATIVE_MAX_DECIMAL_PLACES,
)
.unwrap(),
total_tokens: token::Amount::from_uint(
4_000,
NATIVE_MAX_DECIMAL_PLACES,
)
.unwrap(),
locked_ratio_target: Dec::from_str("0.66666666").unwrap(),
locked_ratio_last: Dec::from_str("0.5").unwrap(),
max_reward_rate: Dec::from_str("0.1").unwrap(),
last_inflation_amount: token::Amount::zero(),
p_gain_nom: Dec::from_str("0.1").unwrap(),
d_gain_nom: Dec::from_str("0.1").unwrap(),
epochs_per_year: 365,
};
dbg!(&controller);

let ValsToUpdate {
locked_ratio: locked_ratio_0,
inflation: inflation_0,
} = controller.clone().run();
println!(
"Round 0: Locked ratio: {locked_ratio_0}, inflation: {}",
inflation_0.to_string_native()
);
assert_eq!(locked_ratio_0, Dec::from_str("0.5").unwrap());
assert_eq!(inflation_0, token::Amount::from_uint(18_264, 0).unwrap());

controller.locked_ratio_last = locked_ratio_0;
controller.last_inflation_amount = inflation_0;
controller.total_tokens += inflation_0;
controller.locked_tokens += inflation_0;

let ValsToUpdate {
locked_ratio: locked_ratio_1,
inflation: inflation_1,
} = controller.clone().run();
println!(
"Round 1: Locked ratio: {locked_ratio_1}, inflation: {}",
inflation_1.to_string_native()
);
assert!(locked_ratio_1 > locked_ratio_0);
assert!(locked_ratio_1 > Dec::from_str("0.5").unwrap());
assert!(locked_ratio_1 < Dec::from_str("0.51").unwrap());
assert_eq!(inflation_1, token::Amount::from_uint(36_528, 0).unwrap());

controller.locked_ratio_last = locked_ratio_1;
controller.last_inflation_amount = inflation_1;
controller.total_tokens += inflation_1;
controller.locked_tokens += inflation_1;

let ValsToUpdate {
locked_ratio: locked_ratio_2,
inflation: inflation_2,
} = controller.run();
println!(
"Round 2: Locked ratio: {locked_ratio_2}, inflation: {}",
inflation_2.to_string_native()
);
assert!(locked_ratio_2 > locked_ratio_1);
assert!(locked_ratio_2 > Dec::from_str("0.5").unwrap());
assert!(locked_ratio_2 < Dec::from_str("0.51").unwrap());
assert_eq!(inflation_2, token::Amount::from_uint(54_792, 0).unwrap());
}

#[test]
fn test_inflation_calc_down() {
let mut controller = RewardsController {
locked_tokens: token::Amount::from_uint(
900,
NATIVE_MAX_DECIMAL_PLACES,
)
.unwrap(),
total_tokens: token::Amount::from_uint(
1_000,
NATIVE_MAX_DECIMAL_PLACES,
)
.unwrap(),
locked_ratio_target: Dec::from_str("0.66666666").unwrap(),
locked_ratio_last: Dec::from_str("0.9").unwrap(),
max_reward_rate: Dec::from_str("0.1").unwrap(),
last_inflation_amount: token::Amount::from_uint(10_000, 0).unwrap(),
p_gain_nom: Dec::from_str("0.1").unwrap(),
d_gain_nom: Dec::from_str("0.1").unwrap(),
epochs_per_year: 365,
};
dbg!(&controller);

let ValsToUpdate {
locked_ratio: locked_ratio_0,
inflation: inflation_0,
} = controller.clone().run();
println!(
"Round 0: Locked ratio: {locked_ratio_0}, inflation: {}",
inflation_0.to_string_native()
);
assert_eq!(locked_ratio_0, Dec::from_str("0.9").unwrap());
assert_eq!(inflation_0, token::Amount::from_uint(3_607, 0).unwrap());

controller.locked_ratio_last = locked_ratio_0;
controller.last_inflation_amount = inflation_0;
controller.total_tokens += inflation_0;
controller.locked_tokens += inflation_0;

let ValsToUpdate {
locked_ratio: locked_ratio_1,
inflation: inflation_1,
} = controller.clone().run();
println!(
"Round 1: Locked ratio: {locked_ratio_1}, inflation: {}",
inflation_1.to_string_native()
);
assert!(locked_ratio_1 > locked_ratio_0);
assert!(locked_ratio_1 > Dec::from_str("0.9").unwrap());
assert!(locked_ratio_1 < Dec::from_str("0.91").unwrap());
assert_eq!(inflation_1, token::Amount::zero());

controller.locked_ratio_last = locked_ratio_1;
controller.last_inflation_amount = inflation_1;
controller.total_tokens += inflation_1;
controller.locked_tokens += inflation_1;

let ValsToUpdate {
locked_ratio: locked_ratio_2,
inflation: inflation_2,
} = controller.run();
println!(
"Round 2: Locked ratio: {locked_ratio_2}, inflation: {}",
inflation_2.to_string_native()
);
assert_eq!(locked_ratio_2, locked_ratio_1);
assert_eq!(inflation_2, token::Amount::zero());
}
}

0 comments on commit c7fbc8c

Please sign in to comment.